Merge "Removing tests used to ensure the transition from HIDL to AIDL works" into main
diff --git a/.gitignore b/.gitignore
index ed653c6..1ad8a24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@
 .idea/
 .vscode/
 *.code-workspace
+
+# Rust artifacts
+**/target/
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 39bbac3..1418e1f 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -83,6 +83,12 @@
 }
 
 prebuilt_etc {
+    name: "android.hardware.consumerir.prebuilt.xml",
+    src: "android.hardware.consumerir.xml",
+    defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
     name: "android.hardware.ethernet.prebuilt.xml",
     src: "android.hardware.ethernet.xml",
     defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 645fa8a..1d513a6 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -53,27 +53,27 @@
 /**
  * Create a shared memory region.
  *
- * Create shared memory region and returns a file descriptor.  The resulting file descriptor can be
- * mmap'ed to process memory space with PROT_READ | PROT_WRITE | PROT_EXEC. Access to shared memory
- * region can be restricted with {@link ASharedMemory_setProt}.
+ * Create a shared memory region and returns a file descriptor.  The resulting file descriptor can be
+ * mapped into the process' memory using mmap(2) with `PROT_READ | PROT_WRITE | PROT_EXEC`.
+ * Access to shared memory regions can be restricted with {@link ASharedMemory_setProt}.
  *
- * Use close() to release the shared memory region.
+ * Use close(2) to release the shared memory region.
  *
  * Use <a href="/reference/android/os/ParcelFileDescriptor">android.os.ParcelFileDescriptor</a>
  * to pass the file descriptor to another process. File descriptors may also be sent to other
- * processes over a Unix domain socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and
+ * processes over a Unix domain socket with sendmsg(2) and `SCM_RIGHTS`. See sendmsg(3) and
  * cmsg(3) man pages for more information.
  *
  * If you intend to share this file descriptor with a child process after
- * calling exec(3), note that you will need to use fcntl(2) with F_SETFD
- * to clear the FD_CLOEXEC flag for this to work on all versions of Android.
+ * calling exec(3), note that you will need to use fcntl(2) with `F_SETFD`
+ * to clear the `FD_CLOEXEC` flag for this to work on all versions of Android.
  *
  * Available since API level 26.
  *
  * \param name an optional name.
  * \param size size of the shared memory region
  * \return file descriptor that denotes the shared memory;
- *         -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
+ *         -1 and sets `errno` on failure, or `-EINVAL` if the error is that size was 0.
  */
 int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
 
@@ -83,7 +83,7 @@
  * Available since API level 26.
  *
  * \param fd file descriptor of the shared memory region
- * \return size in bytes; 0 if fd is not a valid shared memory file descriptor.
+ * \return size in bytes; 0 if `fd` is not a valid shared memory file descriptor.
  */
 size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26);
 
@@ -115,9 +115,9 @@
  * Available since API level 26.
  *
  * \param fd   file descriptor of the shared memory region.
- * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
+ * \param prot any bitwise-or'ed combination of `PROT_READ`, `PROT_WRITE`, `PROT_EXEC` denoting
  *             updated access. Note access can only be removed, but not added back.
- * \return 0 for success, -1 and sets errno on failure.
+ * \return 0 for success, -1 and sets `errno` on failure.
  */
 int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26);
 
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 0f4a6ca..aa67869 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -29,7 +29,6 @@
 #include <binder/Parcel.h>
 #include <binder/RecordedTransaction.h>
 #include <binder/RpcServer.h>
-#include <cutils/compiler.h>
 #include <private/android_filesystem_config.h>
 #include <pthread.h>
 #include <utils/misc.h>
@@ -402,7 +401,7 @@
         }
     }
 
-    if (CC_UNLIKELY(kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION)) {
+    if (kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION) [[unlikely]] {
         Extras* e = mExtras.load(std::memory_order_acquire);
         AutoMutex lock(e->mLock);
         if (mRecordingOn) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index ef0ae4f..3bc4f92 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -23,7 +23,6 @@
 #include <binder/IResultReceiver.h>
 #include <binder/RpcSession.h>
 #include <binder/Stability.h>
-#include <cutils/compiler.h>
 #include <utils/Log.h>
 
 #include <stdio.h>
@@ -166,7 +165,7 @@
         trackedUid = IPCThreadState::self()->getCallingUid();
         AutoMutex _l(sTrackingLock);
         uint32_t trackedValue = sTrackingMap[trackedUid];
-        if (CC_UNLIKELY(trackedValue & LIMIT_REACHED_MASK)) {
+        if (trackedValue & LIMIT_REACHED_MASK) [[unlikely]] {
             if (sBinderProxyThrottleCreate) {
                 return nullptr;
             }
@@ -364,7 +363,7 @@
             Stability::Level required = privateVendor ? Stability::VENDOR
                 : Stability::getLocalLevel();
 
-            if (CC_UNLIKELY(!Stability::check(stability, required))) {
+            if (!Stability::check(stability, required)) [[unlikely]] {
                 ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.",
                       Stability::levelString(stability).c_str(),
                       String8(getInterfaceDescriptor()).c_str(),
@@ -374,7 +373,7 @@
         }
 
         status_t status;
-        if (CC_UNLIKELY(isRpcBinder())) {
+        if (isRpcBinder()) [[unlikely]] {
             status = rpcSession()->transact(sp<IBinder>::fromExisting(this), code, data, reply,
                                             flags);
         } else {
@@ -589,7 +588,9 @@
 }
 
 BpBinder::~BpBinder() {
-    if (CC_UNLIKELY(isRpcBinder())) return;
+    if (isRpcBinder()) [[unlikely]] {
+        return;
+    }
 
     if constexpr (!kEnableKernelIpc) {
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
@@ -603,14 +604,13 @@
     if (mTrackedUid >= 0) {
         AutoMutex _l(sTrackingLock);
         uint32_t trackedValue = sTrackingMap[mTrackedUid];
-        if (CC_UNLIKELY((trackedValue & COUNTING_VALUE_MASK) == 0)) {
+        if ((trackedValue & COUNTING_VALUE_MASK) == 0) [[unlikely]] {
             ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this,
                   binderHandle());
         } else {
-            if (CC_UNLIKELY(
-                (trackedValue & LIMIT_REACHED_MASK) &&
-                ((trackedValue & COUNTING_VALUE_MASK) <= sBinderProxyCountLowWatermark)
-                )) {
+            auto countingValue = trackedValue & COUNTING_VALUE_MASK;
+            if ((trackedValue & LIMIT_REACHED_MASK) &&
+                (countingValue <= sBinderProxyCountLowWatermark)) [[unlikely]] {
                 ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)",
                       getuid(), sBinderProxyCountLowWatermark, mTrackedUid);
                 sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK;
@@ -630,7 +630,9 @@
 }
 
 void BpBinder::onFirstRef() {
-    if (CC_UNLIKELY(isRpcBinder())) return;
+    if (isRpcBinder()) [[unlikely]] {
+        return;
+    }
 
     if constexpr (!kEnableKernelIpc) {
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
@@ -643,7 +645,7 @@
 }
 
 void BpBinder::onLastStrongRef(const void* /*id*/) {
-    if (CC_UNLIKELY(isRpcBinder())) {
+    if (isRpcBinder()) [[unlikely]] {
         (void)rpcSession()->sendDecStrong(this);
         return;
     }
@@ -684,7 +686,9 @@
 bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/)
 {
     // RPC binder doesn't currently support inc from weak binders
-    if (CC_UNLIKELY(isRpcBinder())) return false;
+    if (isRpcBinder()) [[unlikely]] {
+        return false;
+    }
 
     if constexpr (!kEnableKernelIpc) {
         LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 817e0fc..6e3abf5 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -41,7 +41,6 @@
 
 #include <android-base/scopeguard.h>
 #include <cutils/ashmem.h>
-#include <cutils/compiler.h>
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
diff --git a/libs/binder/RpcTlsUtils.cpp b/libs/binder/RpcTlsUtils.cpp
index f3ca02a..d5c86d7 100644
--- a/libs/binder/RpcTlsUtils.cpp
+++ b/libs/binder/RpcTlsUtils.cpp
@@ -21,6 +21,8 @@
 
 #include "Utils.h"
 
+#include <limits>
+
 namespace android {
 
 namespace {
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index f3575cc..ddbcb57 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -79,7 +79,7 @@
                                         altPoll);
     }
 
-    virtual bool isWaiting() { return mSocket.isInPollingState(); }
+    bool isWaiting() override { return mSocket.isInPollingState(); }
 
 private:
     android::RpcTransportFd mSocket;
@@ -88,7 +88,8 @@
 // RpcTransportCtx with TLS disabled.
 class RpcTransportCtxRaw : public RpcTransportCtx {
 public:
-    std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const {
+    std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket,
+                                               FdTrigger*) const override {
         return std::make_unique<RpcTransportRaw>(std::move(socket));
     }
     std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 785f6ce..efb09e9 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -292,7 +292,7 @@
             const std::optional<android::base::function_ref<status_t()>>& altPoll,
             std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
 
-    bool isWaiting() { return mSocket.isInPollingState(); };
+    bool isWaiting() override { return mSocket.isInPollingState(); };
 
 private:
     android::RpcTransportFd mSocket;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 2d05fb2..c432b3a 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -75,7 +75,7 @@
 
 Stability::Level Stability::getLocalLevel() {
 #ifdef __ANDROID_APEX__
-#error APEX can't use libbinder (must use libbinder_ndk)
+#error "APEX can't use libbinder (must use libbinder_ndk)"
 #endif
 
 #ifdef __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index a5c6094..28fb9f1 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -21,6 +21,7 @@
 #include <utils/Mutex.h>
 
 #include <map>
+#include <optional>
 #include <unordered_map>
 #include <variant>
 
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 4e231ed..45e5ace 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -17,7 +17,9 @@
 #pragma once
 
 #include <array>
+#include <limits>
 #include <map> // for legacy reasons
+#include <optional>
 #include <string>
 #include <type_traits>
 #include <variant>
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index b804f7b..2153f16 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 
+#include <bitset>
 #include <mutex>
 #include <thread>
 
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index 8abf04e..b80d116 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -19,6 +19,7 @@
 
 #include <android-base/threads.h>
 
+#include <condition_variable>
 #include <functional>
 #include <memory>
 #include <thread>
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index 5fa2ff6..96b9733 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -18,7 +18,6 @@
 
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
-#include <cutils/compiler.h>
 
 // Set to 1 to enable CallStacks when logging errors
 #define SI_DUMP_CALLSTACKS 0
@@ -218,7 +217,7 @@
     template <typename Function>
     status_t callParcel(const char* name, Function f) const {
         status_t error = f();
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             ALOG(LOG_ERROR, mLogTag, "Failed to %s, (%d: %s)", name, error, strerror(-error));
 #if SI_DUMP_CALLSTACKS
             CallStack callStack(mLogTag);
@@ -265,7 +264,7 @@
         data.writeInterfaceToken(this->getInterfaceDescriptor());
 
         status_t error = writeInputs(&data, std::forward<Args>(args)...);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by writeInputs
             return error;
         }
@@ -273,7 +272,7 @@
         // Send the data Parcel to the remote and retrieve the reply parcel
         Parcel reply;
         error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
 #if SI_DUMP_CALLSTACKS
             CallStack callStack(mLogTag);
@@ -283,7 +282,7 @@
 
         // Read the outputs from the reply Parcel into the output arguments
         error = readOutputs(reply, std::forward<Args>(args)...);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by readOutputs
             return error;
         }
@@ -291,7 +290,7 @@
         // Retrieve the result code from the reply Parcel
         status_t result = NO_ERROR;
         error = reply.readInt32(&result);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             ALOG(LOG_ERROR, mLogTag, "Failed to obtain result");
 #if SI_DUMP_CALLSTACKS
             CallStack callStack(mLogTag);
@@ -315,7 +314,7 @@
         Parcel data;
         data.writeInterfaceToken(this->getInterfaceDescriptor());
         status_t error = writeInputs(&data, std::forward<Args>(args)...);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by writeInputs
             return;
         }
@@ -324,7 +323,7 @@
         Parcel reply;
         error = this->remote()->transact(static_cast<uint32_t>(tag), data, &reply,
                                          IBinder::FLAG_ONEWAY);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             ALOG(LOG_ERROR, mLogTag, "Failed to transact (%d)", error);
 #if SI_DUMP_CALLSTACKS
             CallStack callStack(mLogTag);
@@ -406,7 +405,7 @@
     template <typename T, typename... Remaining>
     status_t writeInputs(Parcel* data, T&& t, Remaining&&... remaining) const {
         status_t error = writeIfInput(data, std::forward<T>(t));
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by writeIfInput
             return error;
         }
@@ -429,7 +428,7 @@
     template <typename T, typename... Remaining>
     status_t readOutputs(const Parcel& reply, T&& t, Remaining&&... remaining) const {
         status_t error = readIfOutput(reply, std::forward<T>(t));
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by readIfOutput
             return error;
         }
@@ -458,7 +457,7 @@
 
         // Read the inputs from the data Parcel into the argument tuple
         status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by read
             return error;
         }
@@ -468,14 +467,14 @@
 
         // Extract the outputs from the argument tuple and write them into the reply Parcel
         error = OutputWriter<ParamTuple>{mLogTag}.writeOutputs(reply, &rawArgs);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by write
             return error;
         }
 
         // Return the result code in the reply Parcel
         error = reply->writeInt32(result);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             ALOG(LOG_ERROR, mLogTag, "Failed to write result");
 #if SI_DUMP_CALLSTACKS
             CallStack callStack(mLogTag);
@@ -500,7 +499,7 @@
 
         // Read the inputs from the data Parcel into the argument tuple
         status_t error = InputReader<ParamTuple>{mLogTag}.readInputs(data, &rawArgs);
-        if (CC_UNLIKELY(error != NO_ERROR)) {
+        if (error != NO_ERROR) [[unlikely]] {
             // A message will have been logged by read
             return error;
         }
@@ -596,7 +595,7 @@
         typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
                 const Parcel& data, RawTuple* args) {
             status_t error = readIfInput<I>(data, args);
-            if (CC_UNLIKELY(error != NO_ERROR)) {
+            if (error != NO_ERROR) [[unlikely]] {
                 // A message will have been logged in read
                 return error;
             }
@@ -694,7 +693,7 @@
         typename std::enable_if<(I < sizeof...(Params)), status_t>::type dispatchArg(
                 Parcel* reply, RawTuple* args) {
             status_t error = writeIfOutput<I>(reply, args);
-            if (CC_UNLIKELY(error != NO_ERROR)) {
+            if (error != NO_ERROR) [[unlikely]] {
                 // A message will have been logged in read
                 return error;
             }
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index ed53891..18769b1 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -31,6 +31,7 @@
 #include <android/binder_parcel.h>
 #include <android/binder_status.h>
 #include <assert.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <cstddef>
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 692cc95..8e95d69 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -59,12 +59,13 @@
     auto basename = Basename(program);
     auto format = R"(dispatch calls to RPC service.
 Usage:
-  %s [-g] <service_name>
+  %s [-g] [-i <ip_address>] <service_name>
     <service_name>: the service to connect to.
   %s [-g] manager
     Runs an RPC-friendly service that redirects calls to servicemanager.
 
   -g: use getService() instead of checkService().
+  -i: use ip_address when setting up the server instead of '127.0.0.1'
 
   If successful, writes port number and a new line character to stdout, and
   blocks until killed.
@@ -74,7 +75,8 @@
     return EX_USAGE;
 }
 
-int Dispatch(const char* name, const ServiceRetriever& serviceRetriever) {
+int Dispatch(const char* name, const ServiceRetriever& serviceRetriever,
+             const char* ip_address = kLocalInetAddress) {
     auto sm = defaultServiceManager();
     if (nullptr == sm) {
         LOG(ERROR) << "No servicemanager";
@@ -91,7 +93,7 @@
         return EX_SOFTWARE;
     }
     unsigned int port;
-    if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+    if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) {
         LOG(ERROR) << "setupInetServer failed: " << statusToString(status);
         return EX_SOFTWARE;
     }
@@ -188,7 +190,8 @@
 // Workaround for b/191059588.
 // TODO(b/191059588): Once we can run RpcServer on single-threaded services,
 //   `servicedispatcher manager` should call Dispatch("manager") directly.
-int wrapServiceManager(const ServiceRetriever& serviceRetriever) {
+int wrapServiceManager(const ServiceRetriever& serviceRetriever,
+                       const char* ip_address = kLocalInetAddress) {
     auto sm = defaultServiceManager();
     if (nullptr == sm) {
         LOG(ERROR) << "No servicemanager";
@@ -212,7 +215,7 @@
     auto rpcServer = RpcServer::make();
     rpcServer->setRootObject(service);
     unsigned int port;
-    if (status_t status = rpcServer->setupInetServer(kLocalInetAddress, 0, &port); status != OK) {
+    if (status_t status = rpcServer->setupInetServer(ip_address, 0, &port); status != OK) {
         LOG(ERROR) << "Unable to set up inet server: " << statusToString(status);
         return EX_SOFTWARE;
     }
@@ -272,11 +275,15 @@
 
     int opt;
     ServiceRetriever serviceRetriever = &android::IServiceManager::checkService;
-    while (-1 != (opt = getopt(argc, argv, "g"))) {
+    char* ip_address = nullptr;
+    while (-1 != (opt = getopt(argc, argv, "gi:"))) {
         switch (opt) {
             case 'g': {
                 serviceRetriever = &android::IServiceManager::getService;
             } break;
+            case 'i': {
+                ip_address = optarg;
+            } break;
             default: {
                 return Usage(argv[0]);
             }
@@ -291,7 +298,15 @@
     auto name = argv[optind];
 
     if (name == "manager"sv) {
-        return wrapServiceManager(serviceRetriever);
+        if (ip_address) {
+            return wrapServiceManager(serviceRetriever, ip_address);
+        } else {
+            return wrapServiceManager(serviceRetriever);
+        }
     }
-    return Dispatch(name, serviceRetriever);
+    if (ip_address) {
+        return Dispatch(name, serviceRetriever, ip_address);
+    } else {
+        return Dispatch(name, serviceRetriever);
+    }
 }
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
index 5079431..0a584bf 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
@@ -26,7 +26,6 @@
 #include <binder/Parcel.h>
 #include <binder/Stability.h>
 
-#include <cutils/compiler.h>
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
diff --git a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
index bf7c613..a6dc182 100644
--- a/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h
@@ -23,7 +23,6 @@
 #include <binder/IResultReceiver.h>
 #include <binder/Parcel.h>
 #include <binder/Stability.h>
-#include <cutils/compiler.h>
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
diff --git a/libs/binder/trusty/fuzzer/Android.bp b/libs/binder/trusty/fuzzer/Android.bp
index 2f1f54b..4f9b5c4 100644
--- a/libs/binder/trusty/fuzzer/Android.bp
+++ b/libs/binder/trusty/fuzzer/Android.bp
@@ -24,6 +24,7 @@
         "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"",
         "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"",
         "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"",
+        "-DTRUSTY_APP_MAX_CONNECTIONS=1",
     ],
 }
 
@@ -35,5 +36,30 @@
         "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"",
         "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"",
         "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"",
+        "-DTRUSTY_APP_MAX_CONNECTIONS=1",
+    ],
+}
+
+cc_fuzz {
+    name: "trusty_binder_fuzzer_multi_connection",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.binder.test.service\"",
+        "-DTRUSTY_APP_UUID=\"d42f06c5-9dc5-455b-9914-cf094116cfa8\"",
+        "-DTRUSTY_APP_FILENAME=\"binder-test-service.syms.elf\"",
+        "-DTRUSTY_APP_MAX_CONNECTIONS=10",
+    ],
+}
+
+cc_fuzz {
+    name: "trusty_binder_rpc_fuzzer_multi_connection",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.binderRpcTestService.V0\"",
+        "-DTRUSTY_APP_UUID=\"87e424e5-69d7-4bbd-8b7c-7e24812cbc94\"",
+        "-DTRUSTY_APP_FILENAME=\"binderRpcTestService.syms.elf\"",
+        "-DTRUSTY_APP_MAX_CONNECTIONS=10",
     ],
 }
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 87f3104..5964281 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -159,7 +159,7 @@
 /// Struct used to contain the buffer.
 pub struct Frame {
     /// A handle to the C buffer interface.
-    pub buffer: AHardwareBuffer,
+    pub buffer: HardwareBuffer,
     /// The time at which the buffer was dispatched.
     pub present_time: Instant,
     /// A fence used for reading/writing safely.
diff --git a/libs/bufferstreams/rust/src/stream_config.rs b/libs/bufferstreams/rust/src/stream_config.rs
index d0c621b..454bdf1 100644
--- a/libs/bufferstreams/rust/src/stream_config.rs
+++ b/libs/bufferstreams/rust/src/stream_config.rs
@@ -33,9 +33,9 @@
 }
 
 impl StreamConfig {
-    /// Tries to create a new AHardwareBuffer from settings in a [StreamConfig].
-    pub fn create_hardware_buffer(&self) -> Option<AHardwareBuffer> {
-        AHardwareBuffer::new(self.width, self.height, self.layers, self.format, self.usage)
+    /// Tries to create a new HardwareBuffer from settings in a [StreamConfig].
+    pub fn create_hardware_buffer(&self) -> Option<HardwareBuffer> {
+        HardwareBuffer::new(self.width, self.height, self.layers, self.format, self.usage)
     }
 }
 
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 6d1dfe8..aab1276 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -58,7 +58,7 @@
     ],
 
     export_shared_lib_headers: [
-        "android.hardware.graphics.common-V4-ndk",
+        "android.hardware.graphics.common-V5-ndk",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
     ],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 61e6657..ce35906 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1307,6 +1307,10 @@
             return "SitedInterstitial";
         case ChromaSiting::COSITED_HORIZONTAL:
             return "CositedHorizontal";
+        case ChromaSiting::COSITED_VERTICAL:
+            return "CositedVertical";
+        case ChromaSiting::COSITED_BOTH:
+            return "CositedBoth";
     }
 }
 
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 1c0439e..5dc195c 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -64,9 +64,17 @@
     void setTargetStatsArray(const std::string& appPackageName, const uint64_t driverVersionCode,
                              const GpuStatsInfo::Stats stats, const uint64_t* values,
                              const uint32_t valueCount) override {
-        for (uint32_t i = 0; i < valueCount; i++) {
-            setTargetStats(appPackageName, driverVersionCode, stats, values[i]);
-        }
+        Parcel data, reply;
+        data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+        data.writeUtf8AsUtf16(appPackageName);
+        data.writeUint64(driverVersionCode);
+        data.writeInt32(static_cast<int32_t>(stats));
+        data.writeUint32(valueCount);
+        data.write(values, valueCount * sizeof(uint64_t));
+
+        remote()->transact(BnGpuService::SET_TARGET_STATS_ARRAY, data, &reply,
+                           IBinder::FLAG_ONEWAY);
     }
 
     void setUpdatableDriverPath(const std::string& driverPath) override {
@@ -164,6 +172,31 @@
 
             return OK;
         }
+        case SET_TARGET_STATS_ARRAY: {
+            CHECK_INTERFACE(IGpuService, data, reply);
+
+            std::string appPackageName;
+            if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+            uint64_t driverVersionCode;
+            if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+            int32_t stats;
+            if ((status = data.readInt32(&stats)) != OK) return status;
+
+            uint32_t valueCount;
+            if ((status = data.readUint32(&valueCount)) != OK) return status;
+
+            std::vector<uint64_t> values(valueCount);
+            if ((status = data.read(values.data(), valueCount * sizeof(uint64_t))) != OK) {
+                return status;
+            }
+
+            setTargetStatsArray(appPackageName, driverVersionCode,
+                                static_cast<GpuStatsInfo::Stats>(stats), values.data(), valueCount);
+
+            return OK;
+        }
         case SET_UPDATABLE_DRIVER_PATH: {
             CHECK_INTERFACE(IGpuService, data, reply);
 
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index e3857d2..45f05d6 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -63,6 +63,7 @@
         SET_UPDATABLE_DRIVER_PATH,
         GET_UPDATABLE_DRIVER_PATH,
         TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
+        SET_TARGET_STATS_ARRAY,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9618502..9893c71 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -31,10 +31,13 @@
 #include <gui/test/CallbackUtils.h>
 #include <private/gui/ComposerService.h>
 #include <private/gui/ComposerServiceAIDL.h>
+#include <tests/utils/ScreenshotUtils.h>
 #include <ui/DisplayMode.h>
 #include <ui/DisplayState.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
 #include <ui/Transform.h>
 
 #include <gtest/gtest.h>
@@ -197,18 +200,23 @@
         ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
               displayState.orientation);
 
+        mRootSurfaceControl = mClient->createSurface(String8("RootTestSurface"), mDisplayWidth,
+                                                     mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+                                                     ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     /*parent*/ nullptr);
+
+        t.setLayerStack(mRootSurfaceControl, ui::DEFAULT_LAYER_STACK)
+                .setLayer(mRootSurfaceControl, std::numeric_limits<int32_t>::max())
+                .show(mRootSurfaceControl)
+                .apply();
+
         mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
                                                  mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                 /*parent*/ nullptr);
-        t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
-                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
-                .show(mSurfaceControl)
-                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
-                .apply();
+                                                 /*parent*/ mRootSurfaceControl->getHandle());
 
-        mCaptureArgs.displayToken = mDisplayToken;
-        mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
+        mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight));
+        mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
     }
 
     void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer,
@@ -295,21 +303,6 @@
         captureBuf->unlock();
     }
 
-    static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
-                                   ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerServiceAIDL::getComposerService();
-        SurfaceComposerClient::Transaction().apply(true);
-
-        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        binder::Status status = sf->captureDisplay(captureArgs, captureListener);
-        status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
-        if (err != NO_ERROR) {
-            return err;
-        }
-        captureResults = captureListener->waitForResults();
-        return fenceStatus(captureResults.fenceResult);
-    }
-
     void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
                      nsecs_t presentTimeDelay) {
         int slot;
@@ -342,11 +335,12 @@
     sp<IBinder> mDisplayToken;
 
     sp<SurfaceControl> mSurfaceControl;
+    sp<SurfaceControl> mRootSurfaceControl;
 
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
 
-    DisplayCaptureArgs mCaptureArgs;
+    LayerCaptureArgs mCaptureArgs;
     ScreenCaptureResults mCaptureResults;
     sp<CountProducerListener> mProducerListener;
 };
@@ -364,7 +358,9 @@
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
     sp<SurfaceControl> updateSurface =
             mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
-                                   PIXEL_FORMAT_RGBA_8888);
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceBufferState,
+                                   /*parent*/ mRootSurfaceControl->getHandle());
     adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
     ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
     sp<IGraphicBufferProducer> igbProducer;
@@ -451,7 +447,7 @@
     Transaction().apply(true /* synchronous */);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -536,7 +532,7 @@
     Transaction().apply(true /* synchronous */);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
 
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b,
@@ -552,16 +548,6 @@
             (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
     int32_t finalCropSideLength = bufferSideLength / 2;
 
-    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                     ISurfaceComposerClient::eFXSurfaceEffect);
-    ASSERT_NE(nullptr, bg.get());
-    Transaction t;
-    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
-            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setColor(bg, half3{0, 0, 0})
-            .setLayer(bg, 0)
-            .apply();
-
     BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
     sp<IGraphicBufferProducer> igbProducer;
     setUpProducer(adapter, igbProducer);
@@ -597,7 +583,7 @@
     Transaction().apply(true /* synchronous */);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
                                                {10, 10, (int32_t)bufferSideLength - 10,
                                                 (int32_t)bufferSideLength - 10}));
@@ -608,17 +594,6 @@
 }
 
 TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
-    // add black background
-    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                     ISurfaceComposerClient::eFXSurfaceEffect);
-    ASSERT_NE(nullptr, bg.get());
-    Transaction t;
-    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
-            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setColor(bg, half3{0, 0, 0})
-            .setLayer(bg, 0)
-            .apply();
-
     Rect windowSize(1000, 1000);
     Rect bufferSize(windowSize);
     Rect bufferCrop(200, 200, 700, 700);
@@ -661,7 +636,7 @@
     // ensure the buffer queue transaction has been committed
     Transaction().apply(true /* synchronous */);
 
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
 
     // Verify cropped region is scaled correctly.
     ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
@@ -676,17 +651,6 @@
 }
 
 TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
-    // add black background
-    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                     ISurfaceComposerClient::eFXSurfaceEffect);
-    ASSERT_NE(nullptr, bg.get());
-    Transaction t;
-    t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
-            .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setColor(bg, half3{0, 0, 0})
-            .setLayer(bg, 0)
-            .apply();
-
     Rect windowSize(1000, 1000);
     Rect bufferSize(500, 500);
     Rect bufferCrop(100, 100, 350, 350);
@@ -729,7 +693,7 @@
     // ensure the buffer queue transaction has been committed
     Transaction().apply(true /* synchronous */);
 
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     // Verify cropped region is scaled correctly.
     ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
     ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
@@ -779,7 +743,7 @@
         Transaction().apply(true /* synchronous */);
     }
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
 
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b,
@@ -815,7 +779,7 @@
         Transaction().apply(true /* synchronous */);
     }
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     // verify we still scale the buffer to the new size (half the screen height)
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b,
@@ -850,12 +814,12 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is green
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 
     mProducerListener->waitOnNumberReleased(1);
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -895,7 +859,7 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -943,7 +907,7 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -993,7 +957,7 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -1051,7 +1015,7 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -1084,13 +1048,13 @@
     transactionCallback.getCallbackData(&callbackData);
 
     // capture screen and verify that it is blue
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 
     mProducerListener->waitOnNumberReleased(2);
     // capture screen and verify that it is red
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
@@ -1186,7 +1150,7 @@
 
     CallbackData callbackData;
     transactionCallback.getCallbackData(&callbackData);
-    ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_NO_FATAL_FAILURE(
             checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
     sync.apply();
@@ -1205,7 +1169,7 @@
                 mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
                                        PIXEL_FORMAT_RGBA_8888,
                                        ISurfaceComposerClient::eFXSurfaceBufferState,
-                                       /*parent*/ nullptr);
+                                       /*parent*/ mRootSurfaceControl->getHandle());
         Transaction()
                 .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1230,7 +1194,7 @@
         CallbackData callbackData;
         transactionCallback.getCallbackData(&callbackData);
         // capture screen and verify that it is red
-        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
         ASSERT_NO_FATAL_FAILURE(
                 checkScreenCapture(255, 0, 0,
                                    {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1248,7 +1212,7 @@
                 mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
                                        PIXEL_FORMAT_RGBA_8888,
                                        ISurfaceComposerClient::eFXSurfaceBufferState,
-                                       /*parent*/ nullptr);
+                                       /*parent*/ mRootSurfaceControl->getHandle());
         Transaction()
                 .setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
                 .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1273,7 +1237,7 @@
         CallbackData callbackData;
         transactionCallback.getCallbackData(&callbackData);
         // capture screen and verify that it is red
-        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
         ASSERT_NO_FATAL_FAILURE(
                 checkScreenCapture(255, 0, 0,
                                    {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1406,7 +1370,7 @@
         ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
 
         Transaction().apply(true /* synchronous */);
-        ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
 
         switch (tr) {
             case ui::Transform::ROT_0:
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e8575a6..6302ff5 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -34,3 +34,10 @@
   description: "Set to true to enable multi-device input: touch and stylus can be active at the same time, but in different windows"
   bug: "211379801"
 }
+
+flag {
+  name: "a11y_crash_on_inconsistent_event_stream"
+  namespace: "accessibility"
+  description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
+  bug: "299977100"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 9725b00..804f96d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -35,7 +35,20 @@
 
 bitflags! {
     /// Source of the input device or input events.
+    #[derive(Debug, PartialEq)]
     pub struct Source: u32 {
+        // Constants from SourceClass, added here for compatibility reasons
+        /// SourceClass::Button
+        const SourceClassButton = SourceClass::Button as u32;
+        /// SourceClass::Pointer
+        const SourceClassPointer = SourceClass::Pointer as u32;
+        /// SourceClass::Navigation
+        const SourceClassNavigation = SourceClass::Navigation as u32;
+        /// SourceClass::Position
+        const SourceClassPosition = SourceClass::Position as u32;
+        /// SourceClass::Joystick
+        const SourceClassJoystick = SourceClass::Joystick as u32;
+
         /// SOURCE_UNKNOWN
         const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN;
         /// SOURCE_KEYBOARD
@@ -190,3 +203,14 @@
         self.bits() & class_bits == class_bits
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::input::SourceClass;
+    use crate::Source;
+    #[test]
+    fn convert_source_class_pointer() {
+        let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap();
+        assert!(source.is_from_class(SourceClass::Pointer));
+    }
+}
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
index e24fa6e..e2eb080 100644
--- a/libs/input/tests/InputVerifier_test.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -20,10 +20,35 @@
 
 namespace android {
 
+using android::base::Result;
+
 TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
     constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
     const std::string name(bytes, sizeof(bytes));
     InputVerifier verifier(name);
 }
 
+TEST(InputVerifierTest, ProcessSourceClassPointer) {
+    InputVerifier verifier("Verify testOnTouchEventScroll");
+
+    std::vector<PointerProperties> properties;
+    properties.push_back({});
+    properties.back().clear();
+    properties.back().id = 0;
+    properties.back().toolType = ToolType::UNKNOWN;
+
+    std::vector<PointerCoords> coords;
+    coords.push_back({});
+    coords.back().clear();
+    coords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 75);
+    coords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 300);
+
+    const Result<void> result =
+            verifier.processMovement(/*deviceId=*/0, AINPUT_SOURCE_CLASS_POINTER,
+                                     AMOTION_EVENT_ACTION_DOWN,
+                                     /*pointerCount=*/properties.size(), properties.data(),
+                                     coords.data(), /*flags=*/0);
+    ASSERT_TRUE(result.ok());
+}
+
 } // namespace android
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index b5ed9e4..c3ac0b7 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -130,19 +130,19 @@
     std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
     ASSERT_GT(model->inputLength(), 0u);
 
-    const int inputLength = model->inputLength();
-    ASSERT_EQ(inputLength, model->inputR().size());
-    ASSERT_EQ(inputLength, model->inputPhi().size());
-    ASSERT_EQ(inputLength, model->inputPressure().size());
-    ASSERT_EQ(inputLength, model->inputOrientation().size());
-    ASSERT_EQ(inputLength, model->inputTilt().size());
+    const size_t inputLength = model->inputLength();
+    ASSERT_EQ(inputLength, static_cast<size_t>(model->inputR().size()));
+    ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPhi().size()));
+    ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPressure().size()));
+    ASSERT_EQ(inputLength, static_cast<size_t>(model->inputOrientation().size()));
+    ASSERT_EQ(inputLength, static_cast<size_t>(model->inputTilt().size()));
 
     ASSERT_TRUE(model->invoke());
 
-    const int outputLength = model->outputLength();
-    ASSERT_EQ(outputLength, model->outputR().size());
-    ASSERT_EQ(outputLength, model->outputPhi().size());
-    ASSERT_EQ(outputLength, model->outputPressure().size());
+    const size_t outputLength = model->outputLength();
+    ASSERT_EQ(outputLength, static_cast<size_t>(model->outputR().size()));
+    ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPhi().size()));
+    ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPressure().size()));
 }
 
 TEST(TfLiteMotionPredictorTest, ModelOutput) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 9fa5569..eab21fb 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -81,11 +81,12 @@
     STANDARD_UNSPECIFIED = 0 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.300   0.600
      *  blue            0.150   0.060
      *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
      * for RGB conversion.
@@ -93,11 +94,12 @@
     STANDARD_BT709 = 1 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.290   0.600
      *  blue            0.150   0.060
      *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
      *  for RGB conversion from the one purely determined by the primaries
@@ -107,11 +109,12 @@
     STANDARD_BT601_625 = 2 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.290   0.600
      *  blue            0.150   0.060
      *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
      * for RGB conversion.
@@ -119,11 +122,12 @@
     STANDARD_BT601_625_UNADJUSTED = 3 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.310   0.595
      *  blue            0.155   0.070
      *  red             0.630   0.340
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      *  KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
      *  for RGB conversion from the one purely determined by the primaries
@@ -133,11 +137,12 @@
     STANDARD_BT601_525 = 4 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.310   0.595
      *  blue            0.155   0.070
      *  red             0.630   0.340
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
      * for RGB conversion (as in SMPTE 240M).
@@ -145,11 +150,12 @@
     STANDARD_BT601_525_UNADJUSTED = 5 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.170   0.797
      *  blue            0.131   0.046
      *  red             0.708   0.292
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
      * for RGB conversion.
@@ -157,11 +163,12 @@
     STANDARD_BT2020 = 6 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.170   0.797
      *  blue            0.131   0.046
      *  red             0.708   0.292
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      *
      * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
      * for RGB conversion using the linear domain.
@@ -169,11 +176,12 @@
     STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
 
     /**
+     * <pre>
      * Primaries:       x      y
      *  green           0.21   0.71
      *  blue            0.14   0.08
      *  red             0.67   0.33
-     *  white (C)       0.310  0.316
+     *  white (C)       0.310  0.316</pre>
      *
      * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
      * for RGB conversion.
@@ -181,11 +189,12 @@
     STANDARD_BT470M = 8 << 16,
 
     /**
+     * <pre>
      * Primaries:       x       y
      *  green           0.243   0.692
      *  blue            0.145   0.049
      *  red             0.681   0.319
-     *  white (C)       0.310   0.316
+     *  white (C)       0.310   0.316</pre>
      *
      * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
      * for RGB conversion.
@@ -194,21 +203,23 @@
 
     /**
      * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
+     * <pre>
      * Primaries:       x       y
      *  green           0.265   0.690
      *  blue            0.150   0.060
      *  red             0.680   0.320
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      */
     STANDARD_DCI_P3 = 10 << 16,
 
     /**
      * Adobe RGB
+     * <pre>
      * Primaries:       x       y
      *  green           0.210   0.710
      *  blue            0.150   0.060
      *  red             0.640   0.330
-     *  white (D65)     0.3127  0.3290
+     *  white (D65)     0.3127  0.3290</pre>
      */
     STANDARD_ADOBE_RGB = 11 << 16,
 
@@ -242,83 +253,86 @@
     TRANSFER_UNSPECIFIED = 0 << 22,
 
     /**
+     * Linear transfer.
+     * <pre>
      * Transfer characteristic curve:
-     *  E = L
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
+     * E = L
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_LINEAR = 1 << 22,
 
     /**
+     * sRGB transfer.
+     * <pre>
      * Transfer characteristic curve:
-     *
      * E = 1.055 * L^(1/2.4) - 0.055  for 0.0031308 <= L <= 1
      *   = 12.92 * L                  for 0 <= L < 0.0031308
      *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *     E - corresponding electrical signal
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_SRGB = 2 << 22,
 
     /**
-     * BT.601 525, BT.601 625, BT.709, BT.2020
-     *
+     * SMPTE 170M transfer.
+     * <pre>
      * Transfer characteristic curve:
-     *  E = 1.099 * L ^ 0.45 - 0.099  for 0.018 <= L <= 1
-     *    = 4.500 * L                 for 0 <= L < 0.018
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
+     * E = 1.099 * L ^ 0.45 - 0.099  for 0.018 <= L <= 1
+     *   = 4.500 * L                 for 0 <= L < 0.018
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_SMPTE_170M = 3 << 22,
 
     /**
-     * Assumed display gamma 2.2.
-     *
+     * Display gamma 2.2.
+     * <pre>
      * Transfer characteristic curve:
-     *  E = L ^ (1/2.2)
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
+     * E = L ^ (1/2.2)
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_GAMMA2_2 = 4 << 22,
 
     /**
-     *  display gamma 2.6.
-     *
+     * Display gamma 2.6.
+     * <pre>
      * Transfer characteristic curve:
-     *  E = L ^ (1/2.6)
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
+     * E = L ^ (1/2.6)
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_GAMMA2_6 = 5 << 22,
 
     /**
-     *  display gamma 2.8.
-     *
+     * Display gamma 2.8.
+     * <pre>
      * Transfer characteristic curve:
-     *  E = L ^ (1/2.8)
-     *      L - luminance of image 0 <= L <= 1 for conventional colorimetry
-     *      E - corresponding electrical signal
+     * E = L ^ (1/2.8)
+     *     L - luminance of image 0 <= L <= 1 for conventional colorimetry
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_GAMMA2_8 = 6 << 22,
 
     /**
-     * SMPTE ST 2084 (Dolby Perceptual Quantizer)
-     *
+     * SMPTE ST 2084 (Dolby Perceptual Quantizer).
+     * <pre>
      * Transfer characteristic curve:
-     *  E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
-     *  c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
-     *  c2 = 32 * 2413 / 4096 = 18.8515625
-     *  c3 = 32 * 2392 / 4096 = 18.6875
-     *  m = 128 * 2523 / 4096 = 78.84375
-     *  n = 0.25 * 2610 / 4096 = 0.1593017578125
-     *      L - luminance of image 0 <= L <= 1 for HDR colorimetry.
-     *          L = 1 corresponds to 10000 cd/m2
-     *      E - corresponding electrical signal
+     * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+     * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+     * c2 = 32 * 2413 / 4096 = 18.8515625
+     * c3 = 32 * 2392 / 4096 = 18.6875
+     * m = 128 * 2523 / 4096 = 78.84375
+     * n = 0.25 * 2610 / 4096 = 0.1593017578125
+     *     L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+     *         L = 1 corresponds to 10000 cd/m2
+     *     E - corresponding electrical signal</pre>
      */
     TRANSFER_ST2084 = 7 << 22,
 
     /**
-     * ARIB STD-B67 Hybrid Log Gamma
-     *
+     * ARIB STD-B67 Hybrid Log Gamma.
+     * <pre>
      * Transfer characteristic curve:
      *  E = r * L^0.5                 for 0 <= L <= 1
      *    = a * ln(L - b) + c         for 1 < L
@@ -328,7 +342,7 @@
      *  r = 0.5
      *      L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
      *          to reference white level of 100 cd/m2
-     *      E - corresponding electrical signal
+     *      E - corresponding electrical signal</pre>
      */
     TRANSFER_HLG = 8 << 22,
 
@@ -384,21 +398,22 @@
     RANGE_EXTENDED = 3 << 27,
 
     /**
-     * scRGB linear encoding:
+     * scRGB linear encoding
      *
      * The red, green, and blue components are stored in extended sRGB space,
      * but are linear, not gamma-encoded.
-     * The RGB primaries and the white point are the same as BT.709.
      *
      * The values are floating point.
      * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
      * Values beyond the range [0.0 - 1.0] would correspond to other colors
      * spaces and/or HDR content.
+     *
+     * Uses extended range, linear transfer and BT.709 standard.
      */
     ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
 
     /**
-     * sRGB gamma encoding:
+     * sRGB gamma encoding
      *
      * The red, green and blue components are stored in sRGB space, and
      * converted to linear space when read, using the SRGB transfer function
@@ -408,29 +423,29 @@
      * The alpha component, if present, is always stored in linear space and
      * is left unmodified when read or written.
      *
-     * Use full range and BT.709 standard.
+     * Uses full range, sRGB transfer BT.709 standard.
      */
     ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
 
     /**
-     * scRGB:
+     * scRGB
      *
      * The red, green, and blue components are stored in extended sRGB space,
      * and gamma-encoded using the SRGB transfer function.
-     * The RGB primaries and the white point are the same as BT.709.
      *
      * The values are floating point.
      * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
      * Values beyond the range [0.0 - 1.0] would correspond to other colors
      * spaces and/or HDR content.
+     *
+     * Uses extended range, sRGB transfer and BT.709 standard.
      */
     ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED
 
     /**
      * Display P3
      *
-     * Use same primaries and white-point as DCI-P3
-     * but sRGB transfer function.
+     * Uses full range, sRGB transfer and D65 DCI-P3 standard.
      */
     ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
 
@@ -439,7 +454,7 @@
      *
      * Ultra High-definition television
      *
-     * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
+     * Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard.
      */
     ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
 
@@ -448,14 +463,15 @@
      *
      * Ultra High-definition television
      *
-     * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+     * Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard.
      */
     ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
 
     /**
      * Adobe RGB
      *
-     * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+     * Uses full range, gamma 2.2 transfer and Adobe RGB standard.
+     *
      * Note: Application is responsible for gamma encoding the data as
      * a 2.2 gamma encoding is not supported in HW.
      */
@@ -464,9 +480,9 @@
     /**
      * JPEG File Interchange Format (JFIF)
      *
-     * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+     * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255.
      *
-     * Use full range, SMPTE 170M transfer and BT.601_625 standard.
+     * Uses full range, SMPTE 170M transfer and BT.601_625 standard.
      */
     ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
 
@@ -475,7 +491,7 @@
      *
      * Standard-definition television, 625 Lines (PAL)
      *
-     * Use limited range, SMPTE 170M transfer and BT.601_625 standard.
+     * Uses limited range, SMPTE 170M transfer and BT.601_625 standard.
      */
     ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
@@ -484,7 +500,7 @@
      *
      * Standard-definition television, 525 Lines (NTSC)
      *
-     * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+     * Uses limited range, SMPTE 170M transfer and BT.601_525 standard.
      */
     ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
@@ -493,7 +509,7 @@
      *
      * Ultra High-definition television
      *
-     * Use full range, BT.709 transfer and BT2020 standard
+     * Uses full range, SMPTE 170M transfer and BT2020 standard.
      */
     ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
 
@@ -502,23 +518,24 @@
      *
      * High-definition television
      *
-     * Use limited range, BT.709 transfer and BT.709 standard.
+     * Uses limited range, SMPTE 170M transfer and BT.709 standard.
      */
     ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
 
     /**
-     * SMPTE EG 432-1 and SMPTE RP 431-2.
+     * SMPTE EG 432-1 and SMPTE RP 431-2
      *
      * Digital Cinema DCI-P3
      *
-     * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+     * Uses full range, gamma 2.6 transfer and D65 DCI-P3 standard.
+     *
      * Note: Application is responsible for gamma encoding the data as
      * a 2.6 gamma encoding is not supported in HW.
      */
     ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
 
     /**
-     * sRGB linear encoding:
+     * sRGB linear encoding
      *
      * The red, green, and blue components are stored in sRGB space, but
      * are linear, not gamma-encoded.
@@ -526,32 +543,34 @@
      *
      * The values are encoded using the full range ([0,255] for 8-bit) for all
      * components.
+     *
+     * Uses full range, linear transfer and BT.709 standard.
      */
     ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
 
     /**
-     * Hybrid Log Gamma encoding:
+     * Hybrid Log Gamma encoding
      *
-     * Use full range, hybrid log gamma transfer and BT2020 standard.
+     * Uses full range, hybrid log gamma transfer and BT2020 standard.
      */
     ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
 
     /**
-     * ITU Hybrid Log Gamma encoding:
+     * ITU Hybrid Log Gamma encoding
      *
-     * Use limited range, hybrid log gamma transfer and BT2020 standard.
+     * Uses limited range, hybrid log gamma transfer and BT2020 standard.
      */
     ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
 
     /**
-     * Depth:
+     * Depth
      *
      * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
      */
     ADATASPACE_DEPTH = 4096,
 
     /**
-     * ISO 16684-1:2011(E) Dynamic Depth:
+     * ISO 16684-1:2011(E) Dynamic Depth
      *
      * Embedded depth metadata following the dynamic depth specification.
      */
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index a2ec57c..6eb3bbc 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,15 +16,17 @@
 
 extern crate nativewindow_bindgen as ffi;
 
-pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use ffi::{AHardwareBuffer, AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
 
-use std::os::raw::c_void;
-use std::ptr;
+use std::fmt::{self, Debug, Formatter};
+use std::mem::ManuallyDrop;
+use std::ptr::{self, NonNull};
 
 /// Wrapper around an opaque C AHardwareBuffer.
-pub struct AHardwareBuffer(*mut ffi::AHardwareBuffer);
+#[derive(PartialEq, Eq)]
+pub struct HardwareBuffer(NonNull<AHardwareBuffer>);
 
-impl AHardwareBuffer {
+impl HardwareBuffer {
     /// Test whether the given format and usage flag combination is allocatable.  If this function
     /// returns true, it means that a buffer with the given description can be allocated on this
     /// implementation, unless resource exhaustion occurs. If this function returns false, it means
@@ -79,13 +81,13 @@
             rfu0: 0,
             rfu1: 0,
         };
-        let mut buffer = ptr::null_mut();
+        let mut ptr = ptr::null_mut();
         // SAFETY: The returned pointer is valid until we drop/deallocate it. The function may fail
         // and return a status, but we check it later.
-        let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut buffer) };
+        let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut ptr) };
 
         if status == 0 {
-            Some(Self(buffer))
+            Some(Self(NonNull::new(ptr).expect("Allocated AHardwareBuffer was null")))
         } else {
             None
         }
@@ -101,9 +103,15 @@
     ///
     /// This function adopts the pointer but does NOT increment the refcount on the buffer. If the
     /// caller uses the pointer after the created object is dropped it will cause a memory leak.
-    pub unsafe fn take_from_raw(buffer_ptr: *mut c_void) -> Self {
-        assert!(!buffer_ptr.is_null());
-        Self(buffer_ptr as *mut ffi::AHardwareBuffer)
+    pub unsafe fn from_raw(buffer_ptr: NonNull<AHardwareBuffer>) -> Self {
+        Self(buffer_ptr)
+    }
+
+    /// Get the internal |AHardwareBuffer| pointer without decrementing the refcount. This can
+    /// be used to provide a pointer to the AHB for a C/C++ API over the FFI.
+    pub fn into_raw(self) -> NonNull<AHardwareBuffer> {
+        let buffer = ManuallyDrop::new(self);
+        buffer.0
     }
 
     /// Get the system wide unique id for an AHardwareBuffer. This function may panic in extreme
@@ -113,7 +121,7 @@
     pub fn id(&self) -> u64 {
         let mut out_id = 0;
         // SAFETY: Neither pointers can be null.
-        let status = unsafe { ffi::AHardwareBuffer_getId(self.0, &mut out_id) };
+        let status = unsafe { ffi::AHardwareBuffer_getId(self.0.as_ref(), &mut out_id) };
         assert_eq!(status, 0, "id() failed for AHardwareBuffer with error code: {status}");
 
         out_id
@@ -161,27 +169,55 @@
             rfu1: 0,
         };
         // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
-        unsafe { ffi::AHardwareBuffer_describe(self.0, &mut buffer_desc) };
+        unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) };
         buffer_desc
     }
 }
 
-impl Drop for AHardwareBuffer {
+impl Drop for HardwareBuffer {
     fn drop(&mut self) {
         // SAFETY: self.0 will never be null. AHardwareBuffers allocated from within Rust will have
         // a refcount of one, and there is a safety warning on taking an AHardwareBuffer from a raw
         // pointer requiring callers to ensure the refcount is managed appropriately.
-        unsafe { ffi::AHardwareBuffer_release(self.0) }
+        unsafe { ffi::AHardwareBuffer_release(self.0.as_ptr()) }
     }
 }
 
+impl Debug for HardwareBuffer {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("HardwareBuffer").field("id", &self.id()).finish()
+    }
+}
+
+impl Clone for HardwareBuffer {
+    fn clone(&self) -> Self {
+        // SAFETY: ptr is guaranteed to be non-null and the acquire can not fail.
+        unsafe { ffi::AHardwareBuffer_acquire(self.0.as_ptr()) };
+        Self(self.0)
+    }
+}
+
+// SAFETY: The underlying *AHardwareBuffers can be moved between threads.
+unsafe impl Send for HardwareBuffer {}
+
+// SAFETY: The underlying *AHardwareBuffers can be used from multiple threads.
+//
+// AHardwareBuffers are backed by C++ GraphicBuffers, which are mostly immutable. The only cases
+// where they are not immutable are:
+//
+//   - reallocation (which is never actually done across the codebase and requires special
+//     privileges/platform code access to do)
+//   - "locking" for reading/writing (which is explicitly allowed to be done across multiple threads
+//     according to the docs on the underlying gralloc calls)
+unsafe impl Sync for HardwareBuffer {}
+
 #[cfg(test)]
-mod ahardwarebuffer_tests {
+mod test {
     use super::*;
 
     #[test]
     fn create_valid_buffer_returns_ok() {
-        let buffer = AHardwareBuffer::new(
+        let buffer = HardwareBuffer::new(
             512,
             512,
             1,
@@ -193,19 +229,12 @@
 
     #[test]
     fn create_invalid_buffer_returns_err() {
-        let buffer = AHardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0));
+        let buffer = HardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0));
         assert!(buffer.is_none());
     }
 
     #[test]
-    #[should_panic]
-    fn take_from_raw_panics_on_null() {
-        // SAFETY: Passing a null pointer is safe, it should just panic.
-        unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) };
-    }
-
-    #[test]
-    fn take_from_raw_allows_getters() {
+    fn from_raw_allows_getters() {
         let buffer_desc = ffi::AHardwareBuffer_Desc {
             width: 1024,
             height: 512,
@@ -225,13 +254,13 @@
 
         // SAFETY: The pointer must be valid because it was just allocated successfully, and we
         // don't use it after calling this.
-        let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) };
+        let buffer = unsafe { HardwareBuffer::from_raw(NonNull::new(raw_buffer_ptr).unwrap()) };
         assert_eq!(buffer.width(), 1024);
     }
 
     #[test]
     fn basic_getters() {
-        let buffer = AHardwareBuffer::new(
+        let buffer = HardwareBuffer::new(
             1024,
             512,
             1,
@@ -252,7 +281,7 @@
 
     #[test]
     fn id_getter() {
-        let buffer = AHardwareBuffer::new(
+        let buffer = HardwareBuffer::new(
             1024,
             512,
             1,
@@ -263,4 +292,38 @@
 
         assert_ne!(0, buffer.id());
     }
+
+    #[test]
+    fn clone() {
+        let buffer = HardwareBuffer::new(
+            1024,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+        )
+        .expect("Buffer with some basic parameters was not created successfully");
+        let buffer2 = buffer.clone();
+
+        assert_eq!(buffer, buffer2);
+    }
+
+    #[test]
+    fn into_raw() {
+        let buffer = HardwareBuffer::new(
+            1024,
+            512,
+            1,
+            AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+            AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+        )
+        .expect("Buffer with some basic parameters was not created successfully");
+        let buffer2 = buffer.clone();
+
+        let raw_buffer = buffer.into_raw();
+        // SAFETY: This is the same pointer we had before.
+        let remade_buffer = unsafe { HardwareBuffer::from_raw(raw_buffer) };
+
+        assert_eq!(remade_buffer, buffer2);
+    }
 }
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
index fbd49c0..876f6c8 100644
--- a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
@@ -21,8 +21,8 @@
 use nativewindow::*;
 
 #[inline]
-fn create_720p_buffer() -> AHardwareBuffer {
-    AHardwareBuffer::new(
+fn create_720p_buffer() -> HardwareBuffer {
+    HardwareBuffer::new(
         1280,
         720,
         1,
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 28aa4dd..8ac0af4 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -28,6 +28,7 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/ShadowSettings.h>
 #include <ui/StretchEffect.h>
 #include <ui/Transform.h>
 
@@ -97,36 +98,6 @@
     half3 solidColor = half3(0.0f, 0.0f, 0.0f);
 };
 
-/*
- * Contains the configuration for the shadows drawn by single layer. Shadow follows
- * material design guidelines.
- */
-struct ShadowSettings {
-    // Boundaries of the shadow.
-    FloatRect boundaries = FloatRect();
-
-    // Color to the ambient shadow. The alpha is premultiplied.
-    vec4 ambientColor = vec4();
-
-    // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
-    // depends on the light position.
-    vec4 spotColor = vec4();
-
-    // Position of the light source used to cast the spot shadow.
-    vec3 lightPos = vec3();
-
-    // Radius of the spot light source. Smaller radius will have sharper edges,
-    // larger radius will have softer shadows
-    float lightRadius = 0.f;
-
-    // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
-    float length = 0.f;
-
-    // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
-    // Otherwise the shadow will only be drawn around the edges of the casting layer.
-    bool casterIsTranslucent = false;
-};
-
 // The settings that RenderEngine requires for correctly rendering a Layer.
 struct LayerSettings {
     // Geometry information
@@ -194,17 +165,6 @@
     return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
 }
 
-static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
-    return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
-            lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
-            lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
-            lhs.casterIsTranslucent == rhs.casterIsTranslucent;
-}
-
-static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
-    return !(operator==(lhs, rhs));
-}
-
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
     if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
         return false;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7dde716..6023808 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -403,7 +403,7 @@
     }
 
     void expectShadowColor(const renderengine::LayerSettings& castingLayer,
-                           const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+                           const ShadowSettings& shadow, const ubyte4& casterColor,
                            const ubyte4& backgroundColor) {
         const Rect casterRect(castingLayer.geometry.boundaries);
         Region casterRegion = Region(casterRect);
@@ -443,8 +443,7 @@
                           backgroundColor.a);
     }
 
-    void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
-                                        const renderengine::ShadowSettings& shadow,
+    void expectShadowColorWithoutCaster(const FloatRect& casterBounds, const ShadowSettings& shadow,
                                         const ubyte4& backgroundColor) {
         const float shadowInset = shadow.length * -1.0f;
         const Rect casterRect(casterBounds);
@@ -463,9 +462,9 @@
                           backgroundColor.a);
     }
 
-    static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
-                                                          bool casterIsTranslucent) {
-        renderengine::ShadowSettings shadow;
+    static ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+                                            bool casterIsTranslucent) {
+        ShadowSettings shadow;
         shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
         shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
         shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
@@ -602,12 +601,10 @@
     void fillGreenColorBufferThenClearRegion();
 
     template <typename SourceVariant>
-    void drawShadow(const renderengine::LayerSettings& castingLayer,
-                    const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
-                    const ubyte4& backgroundColor);
+    void drawShadow(const renderengine::LayerSettings& castingLayer, const ShadowSettings& shadow,
+                    const ubyte4& casterColor, const ubyte4& backgroundColor);
 
-    void drawShadowWithoutCaster(const FloatRect& castingBounds,
-                                 const renderengine::ShadowSettings& shadow,
+    void drawShadowWithoutCaster(const FloatRect& castingBounds, const ShadowSettings& shadow,
                                  const ubyte4& backgroundColor);
 
     // Tonemaps grey values from sourceDataspace -> Display P3 and checks that GPU and CPU
@@ -1337,8 +1334,8 @@
 
 template <typename SourceVariant>
 void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
-                                  const renderengine::ShadowSettings& shadow,
-                                  const ubyte4& casterColor, const ubyte4& backgroundColor) {
+                                  const ShadowSettings& shadow, const ubyte4& casterColor,
+                                  const ubyte4& backgroundColor) {
     renderengine::DisplaySettings settings;
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     settings.physicalDisplay = fullscreenRect();
@@ -1374,7 +1371,7 @@
 }
 
 void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
-                                               const renderengine::ShadowSettings& shadow,
+                                               const ShadowSettings& shadow,
                                                const ubyte4& backgroundColor) {
     renderengine::DisplaySettings settings;
     settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -2103,9 +2100,8 @@
     const float shadowLength = 5.0f;
     Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
     casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              false /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, false /* casterIsTranslucent */);
 
     drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
     expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
@@ -2127,9 +2123,8 @@
     renderengine::LayerSettings castingLayer;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              false /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, false /* casterIsTranslucent */);
 
     drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2152,9 +2147,8 @@
     castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              false /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, false /* casterIsTranslucent */);
 
     drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
     expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2177,9 +2171,8 @@
     castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              false /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, false /* casterIsTranslucent */);
 
     drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
                                                               backgroundColor);
@@ -2204,9 +2197,8 @@
     castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
     castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
     castingLayer.alpha = 1.0f;
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              false /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, false /* casterIsTranslucent */);
 
     drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
                                                               backgroundColor);
@@ -2227,9 +2219,8 @@
     renderengine::LayerSettings castingLayer;
     castingLayer.geometry.boundaries = casterBounds.toFloatRect();
     castingLayer.alpha = 0.5f;
-    renderengine::ShadowSettings settings =
-            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
-                              true /* casterIsTranslucent */);
+    ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+                                                shadowLength, true /* casterIsTranslucent */);
 
     drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
                                                               backgroundColor);
diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h
new file mode 100644
index 0000000..c0b83b8
--- /dev/null
+++ b/libs/ui/include/ui/ShadowSettings.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include "FloatRect.h"
+
+namespace android {
+
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+    // Boundaries of the shadow.
+    FloatRect boundaries = FloatRect();
+
+    // Color to the ambient shadow. The alpha is premultiplied.
+    vec4 ambientColor = vec4();
+
+    // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+    // depends on the light position.
+    vec4 spotColor = vec4();
+
+    // Position of the light source used to cast the spot shadow.
+    vec3 lightPos = vec3();
+
+    // Radius of the spot light source. Smaller radius will have sharper edges,
+    // larger radius will have softer shadows
+    float lightRadius = 0.f;
+
+    // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+    float length = 0.f;
+
+    // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+    // Otherwise the shadow will only be drawn around the edges of the casting layer.
+    bool casterIsTranslucent = false;
+};
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+    return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
+            lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
+            lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+            lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+    return !(operator==(lhs, rhs));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5d1d4af..76729ef 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -182,6 +182,7 @@
 filegroup {
     name: "libinputflinger_base_sources",
     srcs: [
+        "InputDeviceMetricsSource.cpp",
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
@@ -199,6 +200,7 @@
         "libcutils",
         "libinput",
         "liblog",
+        "libstatslog",
         "libutils",
     ],
     header_libs: [
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 8e04150..cefb140 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -17,11 +17,10 @@
 #define LOG_TAG "InputDeviceMetricsCollector"
 #include "InputDeviceMetricsCollector.h"
 
-#include "KeyCodeClassifications.h"
+#include "InputDeviceMetricsSource.h"
 
 #include <android-base/stringprintf.h>
 #include <input/PrintTools.h>
-#include <linux/input.h>
 
 namespace android {
 
@@ -113,96 +112,6 @@
 
 } // namespace
 
-InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
-                                                const NotifyKeyArgs& keyArgs) {
-    if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
-        return InputDeviceUsageSource::UNKNOWN;
-    }
-
-    if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
-        DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
-        return InputDeviceUsageSource::DPAD;
-    }
-
-    if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
-        GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
-        return InputDeviceUsageSource::GAMEPAD;
-    }
-
-    if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
-        return InputDeviceUsageSource::KEYBOARD;
-    }
-
-    return InputDeviceUsageSource::BUTTONS;
-}
-
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
-    LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
-    std::set<InputDeviceUsageSource> sources;
-
-    for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
-        const auto toolType = motionArgs.pointerProperties[i].toolType;
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
-            if (toolType == ToolType::MOUSE) {
-                sources.emplace(InputDeviceUsageSource::MOUSE);
-                continue;
-            }
-            if (toolType == ToolType::FINGER) {
-                sources.emplace(InputDeviceUsageSource::TOUCHPAD);
-                continue;
-            }
-            if (isStylusToolType(toolType)) {
-                sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
-                continue;
-            }
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
-            toolType == ToolType::MOUSE) {
-            sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
-            toolType == ToolType::FINGER) {
-            sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
-            isStylusToolType(toolType)) {
-            sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
-            sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
-            sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
-            sources.emplace(InputDeviceUsageSource::JOYSTICK);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
-            sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
-            sources.emplace(InputDeviceUsageSource::TRACKBALL);
-            continue;
-        }
-        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
-            sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
-            continue;
-        }
-        sources.emplace(InputDeviceUsageSource::UNKNOWN);
-    }
-
-    return sources;
-}
-
-// --- InputDeviceMetricsCollector ---
-
 InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
       : InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
 
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 7775087..9633664 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "InputDeviceMetricsSource.h"
 #include "InputListener.h"
 #include "NotifyArgs.h"
 #include "SyncQueue.h"
@@ -23,7 +24,6 @@
 #include <ftl/mixins.h>
 #include <gui/WindowInfo.h>
 #include <input/InputDevice.h>
-#include <statslog.h>
 #include <chrono>
 #include <functional>
 #include <map>
@@ -52,38 +52,6 @@
     virtual void dump(std::string& dump) = 0;
 };
 
-/**
- * Enum representation of the InputDeviceUsageSource.
- */
-enum class InputDeviceUsageSource : int32_t {
-    UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
-    BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
-    KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
-    DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
-    GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
-    JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
-    MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
-    MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
-    TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
-    TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
-    ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
-    STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
-    STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
-    STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
-    TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
-    TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
-    TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
-
-    ftl_first = UNKNOWN,
-    ftl_last = TRACKBALL,
-};
-
-/** Returns the InputDeviceUsageSource that corresponds to the key event. */
-InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
-
-/** Returns the InputDeviceUsageSources that correspond to the motion event. */
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
-
 /** The logging interface for the metrics collector, injected for testing. */
 class InputDeviceMetricsLogger {
 public:
diff --git a/services/inputflinger/InputDeviceMetricsSource.cpp b/services/inputflinger/InputDeviceMetricsSource.cpp
new file mode 100644
index 0000000..dee4cb8
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.cpp
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#include "InputDeviceMetricsSource.h"
+
+#include "KeyCodeClassifications.h"
+
+#include <android/input.h>
+#include <input/Input.h>
+#include <linux/input.h>
+#include <log/log_main.h>
+
+#include <set>
+
+namespace android {
+
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
+                                                const NotifyKeyArgs& keyArgs) {
+    if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
+        return InputDeviceUsageSource::UNKNOWN;
+    }
+
+    if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
+        DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
+        return InputDeviceUsageSource::DPAD;
+    }
+
+    if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
+        GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
+        return InputDeviceUsageSource::GAMEPAD;
+    }
+
+    if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+        return InputDeviceUsageSource::KEYBOARD;
+    }
+
+    return InputDeviceUsageSource::BUTTONS;
+}
+
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
+    LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
+    std::set<InputDeviceUsageSource> sources;
+
+    for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
+        const auto toolType = motionArgs.pointerProperties[i].toolType;
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
+            if (toolType == ToolType::MOUSE) {
+                sources.emplace(InputDeviceUsageSource::MOUSE);
+                continue;
+            }
+            if (toolType == ToolType::FINGER) {
+                sources.emplace(InputDeviceUsageSource::TOUCHPAD);
+                continue;
+            }
+            if (isStylusToolType(toolType)) {
+                sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
+                continue;
+            }
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
+            toolType == ToolType::MOUSE) {
+            sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
+            toolType == ToolType::FINGER) {
+            sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
+            isStylusToolType(toolType)) {
+            sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
+            sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
+            sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
+            sources.emplace(InputDeviceUsageSource::JOYSTICK);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
+            sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
+            sources.emplace(InputDeviceUsageSource::TRACKBALL);
+            continue;
+        }
+        if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+            sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
+            continue;
+        }
+        sources.emplace(InputDeviceUsageSource::UNKNOWN);
+    }
+
+    return sources;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
new file mode 100644
index 0000000..a6be8f4
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "NotifyArgs.h"
+
+#include <linux/input.h>
+#include <statslog.h>
+
+namespace android {
+
+/**
+ * Enum representation of the InputDeviceUsageSource.
+ */
+enum class InputDeviceUsageSource : int32_t {
+    UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
+    BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
+    KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
+    DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
+    GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
+    JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
+    MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
+    MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
+    TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
+    TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
+    ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
+    STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
+    STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
+    STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
+    TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
+    TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
+    TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
+
+    ftl_first = UNKNOWN,
+    ftl_last = TRACKBALL,
+    // Used by latency fuzzer
+    kMaxValue = ftl_last
+};
+
+/** Returns the InputDeviceUsageSource that corresponds to the key event. */
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
+
+/** Returns the InputDeviceUsageSources that correspond to the motion event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6226a19..276f75c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -25,6 +25,7 @@
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
 #include <binder/Binder.h>
+#include <com_android_input_flags.h>
 #include <ftl/enum.h>
 #include <log/log_event_list.h>
 #if defined(__ANDROID__)
@@ -46,6 +47,8 @@
 #include <queue>
 #include <sstream>
 
+#include "../InputDeviceMetricsSource.h"
+
 #include "Connection.h"
 #include "DebugConfig.h"
 #include "InputDispatcher.h"
@@ -67,6 +70,7 @@
 using android::gui::WindowInfoHandle;
 using android::os::InputEventInjectionResult;
 using android::os::InputEventInjectionSync;
+namespace input_flags = com::android::input::flags;
 
 namespace android::inputdispatcher {
 
@@ -670,11 +674,11 @@
             // This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
             if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
                 android::base::LogSeverity severity = android::base::LogSeverity::FATAL;
-                if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
+                if (!input_flags::a11y_crash_on_inconsistent_event_stream() &&
+                    entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
                     // The Accessibility injected touch exploration event stream
                     // has known inconsistencies, so log ERROR instead of
                     // crashing the device with FATAL.
-                    // TODO(b/299977100): Move a11y severity back to FATAL.
                     severity = android::base::LogSeverity::ERROR;
                 }
                 LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
@@ -3921,6 +3925,16 @@
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
         const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
+    if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS ||
+         options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) &&
+        mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) {
+        LOG(INFO) << __func__
+                  << ": Canceling drag and drop because the pointers for the drag window are being "
+                     "canceled.";
+        sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
+        mDragState.reset();
+    }
+
     if (connection->status == Connection::Status::BROKEN) {
         return;
     }
@@ -3944,7 +3958,6 @@
     android_log_event_list(LOGTAG_INPUT_CANCEL)
             << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
 
-    InputTarget target;
     sp<WindowInfoHandle> windowHandle;
     if (options.displayId) {
         windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
@@ -3952,27 +3965,47 @@
     } else {
         windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
     }
-    if (windowHandle != nullptr) {
-        const WindowInfo* windowInfo = windowHandle->getInfo();
-        target.setDefaultPointerTransform(windowInfo->transform);
-        target.globalScaleFactor = windowInfo->globalScaleFactor;
-    }
-    target.inputChannel = connection->inputChannel;
-    target.flags = InputTarget::Flags::DISPATCH_AS_IS;
 
     const bool wasEmpty = connection->outboundQueue.empty();
 
     for (size_t i = 0; i < cancelationEvents.size(); i++) {
         std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
+        std::vector<InputTarget> targets{};
+        // The target to use if we don't find a window associated with the channel.
+        const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
+                                         .flags = InputTarget::Flags::DISPATCH_AS_IS};
+
         switch (cancelationEventEntry->type) {
             case EventEntry::Type::KEY: {
-                logOutboundKeyDetails("cancel - ",
-                                      static_cast<const KeyEntry&>(*cancelationEventEntry));
+                const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
+                if (windowHandle != nullptr) {
+                    addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+                                          /*pointerIds=*/{}, keyEntry.downTime, targets);
+                } else {
+                    targets.emplace_back(fallbackTarget);
+                }
+                logOutboundKeyDetails("cancel - ", keyEntry);
                 break;
             }
             case EventEntry::Type::MOTION: {
-                logOutboundMotionDetails("cancel - ",
-                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
+                const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
+                if (windowHandle != nullptr) {
+                    std::bitset<MAX_POINTER_ID + 1> pointerIds;
+                    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+                         pointerIndex++) {
+                        pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+                    }
+                    addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+                                          pointerIds, motionEntry.downTime, targets);
+                } else {
+                    targets.emplace_back(fallbackTarget);
+                    const auto it = mDisplayInfos.find(motionEntry.displayId);
+                    if (it != mDisplayInfos.end()) {
+                        targets.back().displayTransform = it->second.transform;
+                        targets.back().setDefaultPointerTransform(it->second.transform);
+                    }
+                }
+                logOutboundMotionDetails("cancel - ", motionEntry);
                 break;
             }
             case EventEntry::Type::FOCUS:
@@ -3992,7 +4025,8 @@
             }
         }
 
-        enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+        if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+        enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0],
                                    InputTarget::Flags::DISPATCH_AS_IS);
     }
 
@@ -4021,23 +4055,33 @@
               connection->getInputChannelName().c_str(), downEvents.size());
     }
 
-    InputTarget target;
     sp<WindowInfoHandle> windowHandle =
             getWindowHandleLocked(connection->inputChannel->getConnectionToken());
-    if (windowHandle != nullptr) {
-        const WindowInfo* windowInfo = windowHandle->getInfo();
-        target.setDefaultPointerTransform(windowInfo->transform);
-        target.globalScaleFactor = windowInfo->globalScaleFactor;
-    }
-    target.inputChannel = connection->inputChannel;
-    target.flags = targetFlags;
 
     const bool wasEmpty = connection->outboundQueue.empty();
     for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
+        std::vector<InputTarget> targets{};
         switch (downEventEntry->type) {
             case EventEntry::Type::MOTION: {
-                logOutboundMotionDetails("down - ",
-                        static_cast<const MotionEntry&>(*downEventEntry));
+                const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry);
+                if (windowHandle != nullptr) {
+                    std::bitset<MAX_POINTER_ID + 1> pointerIds;
+                    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+                         pointerIndex++) {
+                        pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+                    }
+                    addWindowTargetLocked(windowHandle, targetFlags, pointerIds,
+                                          motionEntry.downTime, targets);
+                } else {
+                    targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
+                                                     .flags = targetFlags});
+                    const auto it = mDisplayInfos.find(motionEntry.displayId);
+                    if (it != mDisplayInfos.end()) {
+                        targets.back().displayTransform = it->second.transform;
+                        targets.back().setDefaultPointerTransform(it->second.transform);
+                    }
+                }
+                logOutboundMotionDetails("down - ", motionEntry);
                 break;
             }
 
@@ -4055,7 +4099,8 @@
             }
         }
 
-        enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+        if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+        enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0],
                                    InputTarget::Flags::DISPATCH_AS_IS);
     }
 
@@ -4183,6 +4228,11 @@
     return splitMotionEntry;
 }
 
+void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+    std::scoped_lock _l(mLock);
+    mLatencyTracker.setInputDevices(args.inputDeviceInfos);
+}
+
 void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
     if (debugInboundEventDetails()) {
         ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args.eventTime);
@@ -4395,7 +4445,9 @@
             IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
             !mInputFilterEnabled) {
             const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN;
-            mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime);
+            std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
+            mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime,
+                                          args.deviceId, sources);
         }
 
         needWake = enqueueInboundEventLocked(std::move(newEntry));
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 62e2d58..ee5a797 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -92,7 +92,7 @@
     status_t start() override;
     status_t stop() override;
 
-    void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override{};
+    void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
     void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
     void notifyKey(const NotifyKeyArgs& args) override;
     void notifyMotion(const NotifyMotionArgs& args) override;
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index 3edb638..a7c6d16 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -16,6 +16,8 @@
 
 #include "InputEventTimeline.h"
 
+#include "../InputDeviceMetricsSource.h"
+
 namespace android::inputdispatcher {
 
 ConnectionTimeline::ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime,
@@ -64,8 +66,15 @@
     return !operator==(rhs);
 }
 
-InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime)
-      : isDown(isDown), eventTime(eventTime), readTime(readTime) {}
+InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
+                                       uint16_t vendorId, uint16_t productId,
+                                       std::set<InputDeviceUsageSource> sources)
+      : isDown(isDown),
+        eventTime(eventTime),
+        readTime(readTime),
+        vendorId(vendorId),
+        productId(productId),
+        sources(sources) {}
 
 bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const {
     if (connectionTimelines.size() != rhs.connectionTimelines.size()) {
@@ -80,7 +89,8 @@
             return false;
         }
     }
-    return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime;
+    return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources;
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index daf375d..e9deb2d 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "../InputDeviceMetricsSource.h"
+
 #include <binder/IBinder.h>
 #include <input/Input.h>
 #include <unordered_map>
@@ -73,10 +75,14 @@
 };
 
 struct InputEventTimeline {
-    InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime);
+    InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
+                       uint16_t productId, std::set<InputDeviceUsageSource> sources);
     const bool isDown; // True if this is an ACTION_DOWN event
     const nsecs_t eventTime;
     const nsecs_t readTime;
+    const uint16_t vendorId;
+    const uint16_t productId;
+    const std::set<InputDeviceUsageSource> sources;
 
     struct IBinderHash {
         std::size_t operator()(const sp<IBinder>& b) const {
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index b7c36a8..698bd9f 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "LatencyTracker"
 #include "LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
 
 #include <inttypes.h>
 
@@ -23,6 +24,7 @@
 #include <android-base/stringprintf.h>
 #include <android/os/IInputConstants.h>
 #include <input/Input.h>
+#include <input/InputDevice.h>
 #include <log/log.h>
 
 using android::base::HwTimeoutMultiplier;
@@ -66,7 +68,8 @@
 }
 
 void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime,
-                                   nsecs_t readTime) {
+                                   nsecs_t readTime, DeviceId deviceId,
+                                   const std::set<InputDeviceUsageSource>& sources) {
     reportAndPruneMatureRecords(eventTime);
     const auto it = mTimelines.find(inputEventId);
     if (it != mTimelines.end()) {
@@ -78,7 +81,29 @@
         eraseByValue(mEventTimes, inputEventId);
         return;
     }
-    mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
+
+    // Create an InputEventTimeline for the device ID. The vendorId and productId
+    // can be obtained from the InputDeviceIdentifier of the particular device.
+    const InputDeviceIdentifier* identifier = nullptr;
+    for (auto& inputDevice : mInputDevices) {
+        if (deviceId == inputDevice.getId()) {
+            identifier = &inputDevice.getIdentifier();
+            break;
+        }
+    }
+
+    // If no matching ids can be found for the device from among the input devices connected,
+    // the call to trackListener will be dropped.
+    // Note: there generally isn't expected to be a situation where we can't find an InputDeviceInfo
+    // but a possibility of it is handled in case of race conditions
+    if (identifier == nullptr) {
+        ALOGE("Could not find input device identifier. Dropping call to LatencyTracker.");
+        return;
+    }
+
+    mTimelines.emplace(inputEventId,
+                       InputEventTimeline(isDown, eventTime, readTime, identifier->vendor,
+                                          identifier->product, sources));
     mEventTimes.emplace(eventTime, inputEventId);
 }
 
@@ -171,4 +196,8 @@
             StringPrintf("%s  mEventTimes.size() = %zu\n", prefix, mEventTimes.size());
 }
 
+void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) {
+    mInputDevices = inputDevices;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 4212da8..890d61d 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "../InputDeviceMetricsSource.h"
+
 #include <map>
 #include <unordered_map>
 
@@ -23,6 +25,7 @@
 #include <input/Input.h>
 
 #include "InputEventTimeline.h"
+#include "NotifyArgs.h"
 
 namespace android::inputdispatcher {
 
@@ -49,13 +52,15 @@
      * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
      * must drop all duplicate data.
      */
-    void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
+    void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime,
+                       DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources);
     void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
                             nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
     void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
     std::string dump(const char* prefix) const;
+    void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
 
 private:
     /**
@@ -76,6 +81,7 @@
     std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes;
 
     InputEventTimelineProcessor* mTimelineProcessor;
+    std::vector<InputDeviceInfo> mInputDevices;
     void reportAndPruneMatureRecords(nsecs_t newEventTime);
 };
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index d87a5a7..64e8825 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -50,6 +50,7 @@
         "HardwareProperties_test.cpp",
         "HardwareStateConverter_test.cpp",
         "InputDeviceMetricsCollector_test.cpp",
+        "InputDeviceMetricsSource_test.cpp",
         "InputMapperTest.cpp",
         "InputProcessor_test.cpp",
         "InputProcessorConverter_test.cpp",
@@ -65,7 +66,6 @@
         "SyncQueue_test.cpp",
         "TimerProvider_test.cpp",
         "TestInputListener.cpp",
-        "TestInputListenerMatchers.cpp",
         "TouchpadInputMapper_test.cpp",
         "KeyboardInputMapper_test.cpp",
         "UinputDevice.cpp",
@@ -93,6 +93,7 @@
         },
     },
     static_libs: [
+        "libflagtest",
         "libc++fs",
         "libgmock",
     ],
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index 99a6a1f..b738abf 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -29,8 +29,8 @@
 #include "FakeInputReaderPolicy.h"
 #include "InstrumentedInputReader.h"
 #include "TestConstants.h"
+#include "TestEventMatchers.h"
 #include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
 
 namespace android {
 
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index e630915..b55c9cc 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -22,7 +22,7 @@
 #include "FakePointerController.h"
 #include "InputMapperTest.h"
 #include "InterfaceMocks.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
 
 #define TAG "CursorInputMapper_test"
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 74ce359..d7dc800 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -27,8 +27,8 @@
 #include "InstrumentedInputReader.h"
 #include "NotifyArgs.h"
 #include "TestConstants.h"
+#include "TestEventMatchers.h"
 #include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
 #include "include/gestures.h"
 #include "ui/Rotation.h"
 
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index fdf9ed1..85e055d 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -36,7 +36,6 @@
 
 constexpr auto USAGE_TIMEOUT = 8765309ns;
 constexpr auto TIME = 999999ns;
-constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
 
 constexpr int32_t DEVICE_ID = 3;
 constexpr int32_t DEVICE_ID_2 = 4;
@@ -48,10 +47,6 @@
 const std::string UNIQUE_ID = "Yosemite";
 constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
 constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
-constexpr uint32_t KEY_SOURCES =
-        AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
-constexpr int32_t POINTER_1_DOWN =
-        AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
 InputDeviceIdentifier generateTestIdentifier(int32_t id = DEVICE_ID) {
     InputDeviceIdentifier identifier;
@@ -87,258 +82,6 @@
 
 } // namespace
 
-// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
-
-class DeviceClassificationFixture : public ::testing::Test,
-                                    public ::testing::WithParamInterface<InputDeviceUsageSource> {};
-
-TEST_P(DeviceClassificationFixture, ValidClassifications) {
-    const InputDeviceUsageSource usageSource = GetParam();
-
-    // Use a switch to ensure a test is added for all source classifications.
-    switch (usageSource) {
-        case InputDeviceUsageSource::UNKNOWN: {
-            ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
-                                                       .build()));
-
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::BUTTONS: {
-            ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::KEYBOARD: {
-            ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::DPAD: {
-            ASSERT_EQ(InputDeviceUsageSource::DPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_DPAD_CENTER)
-                                                       .build()));
-
-            ASSERT_EQ(InputDeviceUsageSource::DPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_DPAD_CENTER)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::GAMEPAD: {
-            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_BUTTON_A)
-                                                       .build()));
-
-            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_BUTTON_A)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::JOYSTICK: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
-                                                       .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::MOUSE: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
-                                                AINPUT_SOURCE_MOUSE)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::MOUSE_CAPTURED: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
-                                                AINPUT_SOURCE_MOUSE_RELATIVE)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
-                                                       .x(100)
-                                                       .y(200)
-                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
-                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::TOUCHPAD: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
-                                                       .x(100)
-                                                       .y(200)
-                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
-                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::ROTARY_ENCODER: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
-                                                AINPUT_SOURCE_ROTARY_ENCODER)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
-                                                       .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
-                                                       .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::STYLUS_DIRECT: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
-                                                STYLUS | TOUCHSCREEN)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::STYLUS_INDIRECT: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
-                                                STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::STYLUS_FUSED: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
-                                                AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::TOUCH_NAVIGATION: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
-                                                AINPUT_SOURCE_TOUCH_NAVIGATION)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
-                                                       .x(100)
-                                                       .y(200))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::TOUCHSCREEN: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
-                                                       .x(100)
-                                                       .y(200))
-                                      .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
-                                                       .x(300)
-                                                       .y(400))
-                                      .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::TRACKBALL: {
-            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
-            ASSERT_EQ(srcs,
-                      getUsageSourcesForMotionArgs(
-                              MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
-                                                AINPUT_SOURCE_TRACKBALL)
-                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
-                                                       .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
-                                                       .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
-                                      .build()));
-            break;
-        }
-    }
-}
-
-INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsCollectorDeviceClassificationTest,
-                         DeviceClassificationFixture,
-                         ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
-                         [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
-                             return ftl::enum_string(testParamInfo.param);
-                         });
-
-TEST(InputDeviceMetricsCollectorDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
-    std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
-                                          InputDeviceUsageSource::STYLUS_DIRECT};
-    ASSERT_EQ(srcs,
-              getUsageSourcesForMotionArgs(
-                      MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
-                              .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
-                              .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
-                              .build()));
-}
-
 // --- InputDeviceMetricsCollectorTest ---
 
 class InputDeviceMetricsCollectorTest : public testing::Test, public InputDeviceMetricsLogger {
diff --git a/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
new file mode 100644
index 0000000..84ef52c
--- /dev/null
+++ b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#include "../InputDeviceMetricsSource.h"
+
+#include <NotifyArgsBuilders.h>
+
+#include <android/input.h>
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <linux/input.h>
+
+#include <set>
+
+namespace android {
+
+namespace {
+
+constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
+constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
+constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
+constexpr uint32_t KEY_SOURCES =
+        AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+constexpr int32_t POINTER_1_DOWN =
+        AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+} // namespace
+
+// --- InputDeviceMetricsSourceDeviceClassificationTest ---
+
+class DeviceClassificationFixture : public ::testing::Test,
+                                    public ::testing::WithParamInterface<InputDeviceUsageSource> {};
+
+TEST_P(DeviceClassificationFixture, ValidClassifications) {
+    const InputDeviceUsageSource usageSource = GetParam();
+
+    // Use a switch to ensure a test is added for all source classifications.
+    switch (usageSource) {
+        case InputDeviceUsageSource::UNKNOWN: {
+            ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
+                                                       .build()));
+
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::BUTTONS: {
+            ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::KEYBOARD: {
+            ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::DPAD: {
+            ASSERT_EQ(InputDeviceUsageSource::DPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_DPAD_CENTER)
+                                                       .build()));
+
+            ASSERT_EQ(InputDeviceUsageSource::DPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_DPAD_CENTER)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::GAMEPAD: {
+            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_BUTTON_A)
+                                                       .build()));
+
+            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_BUTTON_A)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::JOYSTICK: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+                                                       .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::MOUSE: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+                                                AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::MOUSE_CAPTURED: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+                                                AINPUT_SOURCE_MOUSE_RELATIVE)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+                                                       .x(100)
+                                                       .y(200)
+                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
+                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::TOUCHPAD: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+                                                       .x(100)
+                                                       .y(200)
+                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
+                                                       .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::ROTARY_ENCODER: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+                                                AINPUT_SOURCE_ROTARY_ENCODER)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+                                                       .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
+                                                       .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::STYLUS_DIRECT: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+                                                STYLUS | TOUCHSCREEN)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::STYLUS_INDIRECT: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+                                                STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::STYLUS_FUSED: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+                                                AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::TOUCH_NAVIGATION: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+                                                AINPUT_SOURCE_TOUCH_NAVIGATION)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+                                                       .x(100)
+                                                       .y(200))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::TOUCHSCREEN: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+                                                       .x(100)
+                                                       .y(200))
+                                      .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
+                                                       .x(300)
+                                                       .y(400))
+                                      .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::TRACKBALL: {
+            std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
+            ASSERT_EQ(srcs,
+                      getUsageSourcesForMotionArgs(
+                              MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+                                                AINPUT_SOURCE_TRACKBALL)
+                                      .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+                                                       .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
+                                                       .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
+                                      .build()));
+            break;
+        }
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsSourceDeviceClassificationTest,
+                         DeviceClassificationFixture,
+                         ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
+                         [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
+                             return ftl::enum_string(testParamInfo.param);
+                         });
+
+TEST(InputDeviceMetricsSourceDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
+    std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
+                                          InputDeviceUsageSource::STYLUS_DIRECT};
+    ASSERT_EQ(srcs,
+              getUsageSourcesForMotionArgs(
+                      MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
+                              .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
+                              .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
+                              .build()));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 703c3f7..2f63b2a 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,7 +16,7 @@
 
 #include "../dispatcher/InputDispatcher.h"
 #include "../BlockingQueue.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
 
 #include <NotifyArgsBuilders.h>
 #include <android-base/properties.h>
@@ -24,7 +24,9 @@
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
+#include <com_android_input_flags.h>
 #include <fcntl.h>
+#include <flag_macros.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/Input.h>
@@ -134,16 +136,6 @@
 
 using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
 
-struct PointF {
-    float x;
-    float y;
-    auto operator<=>(const PointF&) const = default;
-};
-
-inline std::string pointFToString(const PointF& p) {
-    return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
-}
-
 /**
  * Return a DOWN key event with KEYCODE_A.
  */
@@ -156,52 +148,6 @@
     return event;
 }
 
-MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
-    *result_listener << "expected downTime " << downTime << ", but got " << arg.getDownTime();
-    return arg.getDownTime() == downTime;
-}
-
-MATCHER_P(WithSource, source, "InputEvent with specified source") {
-    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
-                     << inputEventSourceToString(arg.getSource());
-    return arg.getSource() == source;
-}
-
-MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
-    *result_listener << "expected flags " << std::hex << flags << ", but got " << arg.getFlags();
-    return arg.getFlags() == flags;
-}
-
-MATCHER_P2(WithCoords, x, y, "MotionEvent with specified coordinates") {
-    if (arg.getPointerCount() != 1) {
-        *result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
-        return false;
-    }
-    const float receivedX = arg.getX(/*pointerIndex=*/0);
-    const float receivedY = arg.getY(/*pointerIndex=*/0);
-    *result_listener << "expected coords (" << x << ", " << y << "), but got (" << receivedX << ", "
-                     << receivedY << ")";
-    return receivedX == x && receivedY == y;
-}
-
-MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") {
-    *result_listener << "expected pointerCount " << pointerCount << ", but got "
-                     << arg.getPointerCount();
-    return arg.getPointerCount() == pointerCount;
-}
-
-MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") {
-    // Build a map for the received pointers, by pointer id
-    std::map<int32_t /*pointerId*/, PointF> actualPointers;
-    for (size_t pointerIndex = 0; pointerIndex < arg.getPointerCount(); pointerIndex++) {
-        const int32_t pointerId = arg.getPointerId(pointerIndex);
-        actualPointers[pointerId] = {arg.getX(pointerIndex), arg.getY(pointerIndex)};
-    }
-    *result_listener << "expected pointers " << dumpMap(pointers, constToString, pointFToString)
-                     << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
-    return pointers == actualPointers;
-}
-
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1451,6 +1397,68 @@
 
 std::atomic<int32_t> FakeWindowHandle::sId{1};
 
+class FakeMonitorReceiver {
+public:
+    FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
+        base::Result<std::unique_ptr<InputChannel>> channel =
+                dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+    }
+
+    sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+                                     expectedFlags);
+    }
+
+    std::optional<int32_t> receiveEvent() {
+        return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+    }
+
+    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
+    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeMotionEvent(
+                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+                      WithDisplayId(expectedDisplayId),
+                      WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
+    }
+
+    void consumeMotionPointerDown(int32_t pointerIdx) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+        mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+                                     /*expectedFlags=*/0);
+    }
+
+    void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+        mInputReceiver->consumeMotionEvent(matcher);
+    }
+
+    MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
+
+    void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+    std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
 static InputEventInjectionResult injectKey(
         InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
         int32_t displayId = ADISPLAY_ID_NONE,
@@ -3764,7 +3772,9 @@
 /**
  * Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
  */
-TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
+TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
+                  REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
+                                                       a11y_crash_on_inconsistent_event_stream))) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window =
             sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -4607,6 +4617,88 @@
     EXPECT_EQ(80, event->getY(0));
 }
 
+TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+    // The monitor will always receive events in the logical display's coordinate space, because
+    // it does not have a window.
+    FakeMonitorReceiver monitor{*mDispatcher, "Monitor", ADISPLAY_ID_DEFAULT};
+
+    // Send down to the first window.
+    mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+    monitor.consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+    // Second pointer goes down on second window.
+    mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT,
+                                                 {PointF{50, 100}, PointF{150, 220}}));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80)));
+    const std::map<int32_t, PointF> expectedMonitorPointers{{0, PointF{100, 400}},
+                                                            {1, PointF{300, 880}}};
+    monitor.consumeMotionEvent(
+            AllOf(WithMotionAction(POINTER_1_DOWN), WithPointers(expectedMonitorPointers)));
+
+    mDispatcher->cancelCurrentTouch();
+
+    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 80)));
+    monitor.consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedMonitorPointers)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinates) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    // Send down to the first window.
+    mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                                 ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+    // The pointer is transferred to the second window, and the second window receives it in the
+    // correct coordinate space.
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverEnterExitWithCorrectCoordinates) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    // Send hover move to the second window, and ensure it shows up as hover enter.
+    mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+                                                 ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+                                           WithCoords(100, 80), WithRawCoords(300, 880)));
+
+    // Touch down at the same location and ensure a hover exit is synthesized.
+    mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
+                                                 ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+                                           WithRawCoords(300, 880)));
+    secondWindow->consumeMotionEvent(
+            AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80), WithRawCoords(300, 880)));
+    secondWindow->assertNoEvents();
+    firstWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverCancelationWithCorrectCoordinates) {
+    auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+    // Send hover enter to second window
+    mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
+                                                 ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+                                           WithCoords(100, 80), WithRawCoords(300, 880)));
+
+    mDispatcher->cancelCurrentTouch();
+
+    secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+                                           WithRawCoords(300, 880)));
+    secondWindow->assertNoEvents();
+    firstWindow->assertNoEvents();
+}
+
 /** Ensure consistent behavior of InputDispatcher in all orientations. */
 class InputDispatcherDisplayOrientationFixture
       : public InputDispatcherDisplayProjectionTest,
@@ -5389,65 +5481,6 @@
     mDispatcher->waitForIdle();
 }
 
-class FakeMonitorReceiver {
-public:
-    FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
-                        int32_t displayId) {
-        base::Result<std::unique_ptr<InputChannel>> channel =
-                dispatcher->createInputMonitor(displayId, name, MONITOR_PID);
-        mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
-    }
-
-    sp<IBinder> getToken() { return mInputReceiver->getToken(); }
-
-    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
-        mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
-                                     expectedFlags);
-    }
-
-    std::optional<int32_t> receiveEvent() {
-        return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
-    }
-
-    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
-
-    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
-        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
-                                     expectedDisplayId, expectedFlags);
-    }
-
-    void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
-        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
-                                     expectedDisplayId, expectedFlags);
-    }
-
-    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
-        mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
-                                     expectedDisplayId, expectedFlags);
-    }
-
-    void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
-        mInputReceiver->consumeMotionEvent(
-                AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
-                      WithDisplayId(expectedDisplayId),
-                      WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
-    }
-
-    void consumeMotionPointerDown(int32_t pointerIdx) {
-        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
-                (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-        mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
-                                     /*expectedFlags=*/0);
-    }
-
-    MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
-
-    void assertNoEvents() { mInputReceiver->assertNoEvents(); }
-
-private:
-    std::unique_ptr<FakeInputReceiver> mInputReceiver;
-};
-
 using InputDispatcherMonitorTest = InputDispatcherTest;
 
 /**
@@ -5463,7 +5496,7 @@
     sp<FakeWindowHandle> window =
             sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
 
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -5505,7 +5538,7 @@
                                                              "Fake Window", ADISPLAY_ID_DEFAULT);
     mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
 
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5515,7 +5548,7 @@
 }
 
 TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -5549,7 +5582,7 @@
     window->setWindowOffset(20, 40);
     window->setWindowTransform(0, 1, -1, 0);
 
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5562,7 +5595,7 @@
 
 TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     ASSERT_EQ(InputEventInjectionResult::FAILED,
               injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -6452,9 +6485,9 @@
 // Test per-display input monitors for motion event.
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
     FakeMonitorReceiver monitorInPrimary =
-            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+            FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
     FakeMonitorReceiver monitorInSecondary =
-            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+            FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test touch down on primary display.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6497,9 +6530,9 @@
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
     // Input monitor per display.
     FakeMonitorReceiver monitorInPrimary =
-            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+            FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
     FakeMonitorReceiver monitorInSecondary =
-            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+            FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test inject a key down.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
@@ -6535,9 +6568,9 @@
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
     FakeMonitorReceiver monitorInPrimary =
-            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+            FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
     FakeMonitorReceiver monitorInSecondary =
-            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+            FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test touch down on primary display.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -7461,7 +7494,7 @@
 TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
     mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
 
-    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
 
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
               injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -9318,6 +9351,76 @@
     mSecondWindow->assertNoEvents();
 }
 
+/**
+ * Start drag and drop with a pointer whose id is not 0, cancel the current touch, and ensure drag
+ * and drop is also canceled. Then inject a simple gesture, and ensure dispatcher does not crash.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) {
+    // Down on second window
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionDown());
+    ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionDown());
+
+    // Down on first window
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
+                    .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+    ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionMove());
+    ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionPointerDown(1));
+
+    // Start drag on first window
+    ASSERT_TRUE(startDrag(/*sendDown=*/false, AINPUT_SOURCE_TOUCHSCREEN));
+
+    // Trigger cancel
+    mDispatcher->cancelCurrentTouch();
+    ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel());
+    ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel());
+    ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel());
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // The D&D finished with nullptr
+    mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
+
+    // Remove drag window
+    mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+
+    // Inject a simple gesture, ensure dispatcher not crashed
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               PointF{50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+
+    const MotionEvent moveEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(*mDispatcher, moveEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionMove());
+
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
+}
+
 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 6032e30..64ae9e8 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -31,8 +31,8 @@
 #include <SensorInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
+#include <TestEventMatchers.h>
 #include <TestInputListener.h>
-#include <TestInputListenerMatchers.h>
 #include <TouchInputMapper.h>
 #include <UinputDevice.h>
 #include <VibratorInputMapper.h>
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index fa149db..6606de8 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -15,11 +15,14 @@
  */
 
 #include "../dispatcher/LatencyTracker.h"
+#include "../InputDeviceMetricsSource.h"
 
 #include <android-base/properties.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
+#include <gui/constants.h>
 #include <inttypes.h>
+#include <linux/input.h>
 #include <log/log.h>
 
 #define TAG "LatencyTracker_test"
@@ -30,6 +33,29 @@
 
 namespace android::inputdispatcher {
 
+namespace {
+
+constexpr DeviceId DEVICE_ID = 100;
+
+static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId,
+                                              DeviceId deviceId) {
+    InputDeviceIdentifier identifier;
+    identifier.vendor = vendorId;
+    identifier.product = productId;
+    auto info = InputDeviceInfo();
+    info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
+                    /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+    return info;
+}
+
+void setDefaultInputDeviceInfo(LatencyTracker& tracker) {
+    InputDeviceInfo deviceInfo = generateTestDeviceInfo(
+            /*vendorId=*/0, /*productId=*/0, DEVICE_ID);
+    tracker.setInputDevices({deviceInfo});
+}
+
+} // namespace
+
 const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
         android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
         HwTimeoutMultiplier());
@@ -38,7 +64,10 @@
     InputEventTimeline t(
             /*isDown=*/true,
             /*eventTime=*/2,
-            /*readTime=*/3);
+            /*readTime=*/3,
+            /*vendorId=*/0,
+            /*productId=*/0,
+            /*sources=*/{InputDeviceUsageSource::UNKNOWN});
     ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8);
     std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
     graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9;
@@ -60,6 +89,7 @@
         connection2 = sp<BBinder>::make();
 
         mTracker = std::make_unique<LatencyTracker>(this);
+        setDefaultInputDeviceInfo(*mTracker);
     }
     void TearDown() override {}
 
@@ -88,7 +118,8 @@
     const nsecs_t triggerEventTime =
             lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
     mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime,
-                            /*readTime=*/3);
+                            /*readTime=*/3, DEVICE_ID,
+                            /*sources=*/{InputDeviceUsageSource::UNKNOWN});
 }
 
 void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
@@ -138,9 +169,11 @@
  */
 TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
     mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2,
-                            /*readTime=*/3);
+                            /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
     triggerEventReporting(/*eventTime=*/2);
-    assertReceivedTimeline(InputEventTimeline{false, 2, 3});
+    assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2,
+                                              /*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
+                                              /*sources=*/{InputDeviceUsageSource::UNKNOWN}});
 }
 
 /**
@@ -171,7 +204,8 @@
 
     const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin();
 
-    mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+    mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+                            DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
     mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime,
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
@@ -191,8 +225,10 @@
 
     // In the following 2 calls to trackListener, the inputEventId's are the same, but event times
     // are different.
-    mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime);
-    mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime);
+    mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID,
+                            {InputDeviceUsageSource::UNKNOWN});
+    mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID,
+                            {InputDeviceUsageSource::UNKNOWN});
 
     triggerEventReporting(/*eventTime=*/2);
     // Since we sent duplicate input events, the tracker should just delete all of them, because it
@@ -205,7 +241,10 @@
     InputEventTimeline timeline1(
             /*isDown*/ true,
             /*eventTime*/ 2,
-            /*readTime*/ 3);
+            /*readTime*/ 3,
+            /*vendorId=*/0,
+            /*productId=*/0,
+            /*sources=*/{InputDeviceUsageSource::UNKNOWN});
     timeline1.connectionTimelines.emplace(connection1,
                                           ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7,
                                                              /*finishTime*/ 8));
@@ -219,7 +258,10 @@
     InputEventTimeline timeline2(
             /*isDown=*/false,
             /*eventTime=*/20,
-            /*readTime=*/30);
+            /*readTime=*/30,
+            /*vendorId=*/0,
+            /*productId=*/0,
+            /*sources=*/{InputDeviceUsageSource::UNKNOWN});
     timeline2.connectionTimelines.emplace(connection2,
                                           ConnectionTimeline(/*deliveryTime=*/60,
                                                              /*consumeTime=*/70,
@@ -232,10 +274,10 @@
 
     // Start processing first event
     mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime,
-                            timeline1.readTime);
+                            timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
     // Start processing second event
     mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime,
-                            timeline2.readTime);
+                            timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
     mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime,
                                  connectionTimeline1.consumeTime, connectionTimeline1.finishTime);
 
@@ -261,9 +303,11 @@
 
     for (size_t i = 1; i <= 100; i++) {
         mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime,
-                                timeline.readTime);
-        expectedTimelines.push_back(
-                InputEventTimeline{timeline.isDown, timeline.eventTime, timeline.readTime});
+                                timeline.readTime, /*deviceId=*/DEVICE_ID,
+                                /*sources=*/{InputDeviceUsageSource::UNKNOWN});
+        expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime,
+                                                       timeline.readTime, timeline.vendorId,
+                                                       timeline.productId, timeline.sources});
     }
     // Now, complete the first event that was sent.
     mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime,
@@ -289,10 +333,38 @@
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
 
-    mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+    mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime,
+                            DEVICE_ID, {InputDeviceUsageSource::UNKNOWN});
     triggerEventReporting(expected.eventTime);
-    assertReceivedTimeline(
-            InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
+    assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime,
+                                              expected.readTime, expected.vendorId,
+                                              expected.productId, expected.sources});
+}
+
+/**
+ * Check that LatencyTracker has the received timeline that contains the correctly
+ * resolved product ID, vendor ID and source for a particular device ID from
+ * among a list of devices.
+ */
+TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) {
+    constexpr int32_t inputEventId = 1;
+    InputEventTimeline timeline(
+            /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3,
+            /*vendorId=*/50, /*productId=*/60,
+            /*sources=*/
+            {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT});
+    InputDeviceInfo deviceInfo1 = generateTestDeviceInfo(
+            /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1);
+    InputDeviceInfo deviceInfo2 = generateTestDeviceInfo(
+            /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID);
+
+    mTracker->setInputDevices({deviceInfo1, deviceInfo2});
+    mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime,
+                            DEVICE_ID,
+                            {InputDeviceUsageSource::TOUCHSCREEN,
+                             InputDeviceUsageSource::STYLUS_DIRECT});
+    triggerEventReporting(timeline.eventTime);
+    assertReceivedTimeline(timeline);
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
new file mode 100644
index 0000000..ee6ff53
--- /dev/null
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include <cmath>
+#include <compare>
+
+#include <android-base/stringprintf.h>
+#include <android/input.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/PrintTools.h>
+
+#include "NotifyArgs.h"
+#include "TestConstants.h"
+
+namespace android {
+
+struct PointF {
+    float x;
+    float y;
+    auto operator<=>(const PointF&) const = default;
+};
+
+inline std::string pointFToString(const PointF& p) {
+    return std::string("(") + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";
+}
+
+/// Source
+class WithSourceMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithSourceMatcher(uint32_t source) : mSource(source) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        return mSource == args.source;
+    }
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mSource == args.source;
+    }
+
+    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+        return mSource == event.getSource();
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with source " << inputEventSourceToString(mSource);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong source"; }
+
+private:
+    const uint32_t mSource;
+};
+
+inline WithSourceMatcher WithSource(uint32_t source) {
+    return WithSourceMatcher(source);
+}
+
+/// Key action
+class WithKeyActionMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mAction == args.action;
+    }
+
+    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+        return mAction == event.getAction();
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with key action " << KeyEvent::actionToString(mAction);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+    const int32_t mAction;
+};
+
+inline WithKeyActionMatcher WithKeyAction(int32_t action) {
+    return WithKeyActionMatcher(action);
+}
+
+/// Motion action
+class WithMotionActionMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        bool matches = mAction == args.action;
+        if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
+            matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
+        }
+        return matches;
+    }
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+        bool matches = mAction == event.getAction();
+        if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+            matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+        }
+        return matches;
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with motion action " << MotionEvent::actionToString(mAction);
+        if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+            *os << " and FLAG_CANCELED";
+        }
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+    const int32_t mAction;
+};
+
+inline WithMotionActionMatcher WithMotionAction(int32_t action) {
+    return WithMotionActionMatcher(action);
+}
+
+/// Display Id
+class WithDisplayIdMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        return mDisplayId == args.displayId;
+    }
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mDisplayId == args.displayId;
+    }
+
+    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+        return mDisplayId == event.getDisplayId();
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
+
+private:
+    const int32_t mDisplayId;
+};
+
+inline WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+    return WithDisplayIdMatcher(displayId);
+}
+
+/// Device Id
+class WithDeviceIdMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        return mDeviceId == args.deviceId;
+    }
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mDeviceId == args.deviceId;
+    }
+
+    bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
+        return mDeviceId == args.deviceId;
+    }
+
+    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+        return mDeviceId == event.getDeviceId();
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+    const int32_t mDeviceId;
+};
+
+inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+    return WithDeviceIdMatcher(deviceId);
+}
+
+/// Flags
+class WithFlagsMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithFlagsMatcher(int32_t flags) : mFlags(flags) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        return mFlags == args.flags;
+    }
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mFlags == args.flags;
+    }
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+        return mFlags == event.getFlags();
+    }
+
+    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+        return mFlags == event.getFlags();
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with flags " << base::StringPrintf("0x%x", mFlags);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong flags"; }
+
+private:
+    const int32_t mFlags;
+};
+
+inline WithFlagsMatcher WithFlags(int32_t flags) {
+    return WithFlagsMatcher(flags);
+}
+
+/// DownTime
+class WithDownTimeMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithDownTimeMatcher(nsecs_t downTime) : mDownTime(downTime) {}
+
+    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+        return mDownTime == args.downTime;
+    }
+
+    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
+        return mDownTime == args.downTime;
+    }
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+        return mDownTime == event.getDownTime();
+    }
+
+    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
+        return mDownTime == event.getDownTime();
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "with down time " << mDownTime; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong down time"; }
+
+private:
+    const nsecs_t mDownTime;
+};
+
+inline WithDownTimeMatcher WithDownTime(nsecs_t downTime) {
+    return WithDownTimeMatcher(downTime);
+}
+
+/// Coordinate matcher
+class WithCoordsMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithCoordsMatcher(size_t pointerIndex, float x, float y)
+          : mPointerIndex(pointerIndex), mX(x), mY(y) {}
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+        if (mPointerIndex >= event.getPointerCount()) {
+            *os << "Pointer index " << mPointerIndex << " is out of bounds";
+            return false;
+        }
+
+        bool matches = mX == event.getX(mPointerIndex) && mY == event.getY(mPointerIndex);
+        if (!matches) {
+            *os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
+                << ", but got (" << event.getX(mPointerIndex) << ", " << event.getY(mPointerIndex)
+                << ")";
+        }
+        return matches;
+    }
+
+    bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+        if (mPointerIndex >= event.pointerCoords.size()) {
+            *os << "Pointer index " << mPointerIndex << " is out of bounds";
+            return false;
+        }
+
+        bool matches = mX == event.pointerCoords[mPointerIndex].getX() &&
+                mY == event.pointerCoords[mPointerIndex].getY();
+        if (!matches) {
+            *os << "expected coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex
+                << ", but got (" << event.pointerCoords[mPointerIndex].getX() << ", "
+                << event.pointerCoords[mPointerIndex].getY() << ")";
+        }
+        return matches;
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with coords (" << mX << ", " << mY << ") at pointer index " << mPointerIndex;
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong coords"; }
+
+private:
+    const size_t mPointerIndex;
+    const float mX;
+    const float mY;
+};
+
+inline WithCoordsMatcher WithCoords(float x, float y) {
+    return WithCoordsMatcher(0, x, y);
+}
+
+inline WithCoordsMatcher WithPointerCoords(size_t pointerIndex, float x, float y) {
+    return WithCoordsMatcher(pointerIndex, x, y);
+}
+
+/// Raw coordinate matcher
+class WithRawCoordsMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithRawCoordsMatcher(size_t pointerIndex, float rawX, float rawY)
+          : mPointerIndex(pointerIndex), mRawX(rawX), mRawY(rawY) {}
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+        if (mPointerIndex >= event.getPointerCount()) {
+            *os << "Pointer index " << mPointerIndex << " is out of bounds";
+            return false;
+        }
+
+        bool matches =
+                mRawX == event.getRawX(mPointerIndex) && mRawY == event.getRawY(mPointerIndex);
+        if (!matches) {
+            *os << "expected raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
+                << mPointerIndex << ", but got (" << event.getRawX(mPointerIndex) << ", "
+                << event.getRawY(mPointerIndex) << ")";
+        }
+        return matches;
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with raw coords (" << mRawX << ", " << mRawY << ") at pointer index "
+            << mPointerIndex;
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong raw coords"; }
+
+private:
+    const size_t mPointerIndex;
+    const float mRawX;
+    const float mRawY;
+};
+
+inline WithRawCoordsMatcher WithRawCoords(float rawX, float rawY) {
+    return WithRawCoordsMatcher(0, rawX, rawY);
+}
+
+inline WithRawCoordsMatcher WithPointerRawCoords(size_t pointerIndex, float rawX, float rawY) {
+    return WithRawCoordsMatcher(pointerIndex, rawX, rawY);
+}
+
+/// Pointer count
+class WithPointerCountMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithPointerCountMatcher(size_t pointerCount) : mPointerCount(pointerCount) {}
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+        if (event.getPointerCount() != mPointerCount) {
+            *os << "expected pointer count " << mPointerCount << ", but got "
+                << event.getPointerCount();
+            return false;
+        }
+        return true;
+    }
+
+    bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+        if (event.pointerCoords.size() != mPointerCount) {
+            *os << "expected pointer count " << mPointerCount << ", but got "
+                << event.pointerCoords.size();
+            return false;
+        }
+        return true;
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "with pointer count " << mPointerCount; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointer count"; }
+
+private:
+    const size_t mPointerCount;
+};
+
+inline WithPointerCountMatcher WithPointerCount(size_t pointerCount) {
+    return WithPointerCountMatcher(pointerCount);
+}
+
+/// Pointers matcher
+class WithPointersMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithPointersMatcher(std::map<int32_t, PointF> pointers) : mPointers(pointers) {}
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream* os) const {
+        std::map<int32_t, PointF> actualPointers;
+        for (size_t pointerIndex = 0; pointerIndex < event.getPointerCount(); pointerIndex++) {
+            const int32_t pointerId = event.getPointerId(pointerIndex);
+            actualPointers[pointerId] = {event.getX(pointerIndex), event.getY(pointerIndex)};
+        }
+
+        if (mPointers != actualPointers) {
+            *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
+                << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+            return false;
+        }
+        return true;
+    }
+
+    bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+        std::map<int32_t, PointF> actualPointers;
+        for (size_t pointerIndex = 0; pointerIndex < event.pointerCoords.size(); pointerIndex++) {
+            const int32_t pointerId = event.pointerProperties[pointerIndex].id;
+            actualPointers[pointerId] = {event.pointerCoords[pointerIndex].getX(),
+                                         event.pointerCoords[pointerIndex].getY()};
+        }
+
+        if (mPointers != actualPointers) {
+            *os << "expected pointers " << dumpMap(mPointers, constToString, pointFToString)
+                << ", but got " << dumpMap(actualPointers, constToString, pointFToString);
+            return false;
+        }
+        return true;
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with pointers " << dumpMap(mPointers, constToString, pointFToString);
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong pointers"; }
+
+private:
+    const std::map<int32_t, PointF> mPointers;
+};
+
+inline WithPointersMatcher WithPointers(
+        const std::map<int32_t /*id*/, PointF /*coords*/>& pointers) {
+    return WithPointersMatcher(pointers);
+}
+
+MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
+    *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
+    return arg.keyCode == keyCode;
+}
+
+MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
+    return arg.getRepeatCount() == repeatCount;
+}
+
+MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
+    const auto argPointerId = arg.pointerProperties[index].id;
+    *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
+    return argPointerId == id;
+}
+
+MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
+    const auto argX = arg.xCursorPosition;
+    const auto argY = arg.yCursorPosition;
+    *result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
+                     << ", " << argY << ")";
+    return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
+}
+
+MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
+    const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+    const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+    *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
+                     << ", " << argY << ")";
+    return argX == x && argY == y;
+}
+
+MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
+           "InputEvent with specified touchpad gesture offset") {
+    const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
+    const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
+    const double xDiff = fabs(argGestureX - dx);
+    const double yDiff = fabs(argGestureY - dy);
+    *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
+                     << ", but got (" << argGestureX << ", " << argGestureY << ")";
+    return xDiff <= epsilon && yDiff <= epsilon;
+}
+
+MATCHER_P3(WithGestureScrollDistance, x, y, epsilon,
+           "InputEvent with specified touchpad gesture scroll distance") {
+    const auto argXDistance =
+            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE);
+    const auto argYDistance =
+            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE);
+    const double xDiff = fabs(argXDistance - x);
+    const double yDiff = fabs(argYDistance - y);
+    *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon
+                     << ", but got (" << argXDistance << ", " << argYDistance << ")";
+    return xDiff <= epsilon && yDiff <= epsilon;
+}
+
+MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon,
+           "InputEvent with specified touchpad pinch gesture scale factor") {
+    const auto argScaleFactor =
+            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR);
+    *result_listener << "expected gesture scale factor " << factor << " within " << epsilon
+                     << " but got " << argScaleFactor;
+    return fabs(argScaleFactor - factor) <= epsilon;
+}
+
+MATCHER_P(WithGestureSwipeFingerCount, count,
+          "InputEvent with specified touchpad swipe finger count") {
+    const auto argFingerCount =
+            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT);
+    *result_listener << "expected gesture swipe finger count " << count << " but got "
+                     << argFingerCount;
+    return fabs(argFingerCount - count) <= EPSILON;
+}
+
+MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
+    const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
+    *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
+    return argPressure == pressure;
+}
+
+MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
+    const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+    const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+    *result_listener << "expected touch dimensions " << maj << " major x " << min
+                     << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+    return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
+    const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
+    const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
+    *result_listener << "expected tool dimensions " << maj << " major x " << min
+                     << " minor, but got " << argMajor << " major x " << argMinor << " minor";
+    return argMajor == maj && argMinor == min;
+}
+
+MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
+    const auto argToolType = arg.pointerProperties[0].toolType;
+    *result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
+                     << ftl::enum_string(argToolType);
+    return argToolType == toolType;
+}
+
+MATCHER_P2(WithPointerToolType, pointer, toolType,
+           "InputEvent with specified tool type for pointer") {
+    const auto argToolType = arg.pointerProperties[pointer].toolType;
+    *result_listener << "expected pointer " << pointer << " to have tool type "
+                     << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
+    return argToolType == toolType;
+}
+
+MATCHER_P(WithMotionClassification, classification,
+          "InputEvent with specified MotionClassification") {
+    *result_listener << "expected classification " << motionClassificationToString(classification)
+                     << ", but got " << motionClassificationToString(arg.classification);
+    return arg.classification == classification;
+}
+
+MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
+    *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
+    return arg.buttonState == buttons;
+}
+
+MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
+    *result_listener << "expected action button " << actionButton << ", but got "
+                     << arg.actionButton;
+    return arg.actionButton == actionButton;
+}
+
+MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
+    *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
+    return arg.eventTime == eventTime;
+}
+
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+    *result_listener << "expected down time " << downTime << ", but got " << arg.downTime;
+    return arg.downTime == downTime;
+}
+
+MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
+    *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
+                     << ", but got " << arg.xPrecision << " and " << arg.yPrecision;
+    return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.cpp b/services/inputflinger/tests/TestInputListenerMatchers.cpp
deleted file mode 100644
index 1464e60..0000000
--- a/services/inputflinger/tests/TestInputListenerMatchers.cpp
+++ /dev/null
@@ -1,37 +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.
- */
-
-#include "TestInputListenerMatchers.h"
-
-namespace android {
-
-WithKeyActionMatcher WithKeyAction(int32_t action) {
-    return WithKeyActionMatcher(action);
-}
-
-WithMotionActionMatcher WithMotionAction(int32_t action) {
-    return WithMotionActionMatcher(action);
-}
-
-WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
-    return WithDisplayIdMatcher(displayId);
-}
-
-WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
-    return WithDeviceIdMatcher(deviceId);
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
deleted file mode 100644
index 31ad569..0000000
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#pragma once
-
-#include <cmath>
-
-#include <android/input.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <input/Input.h>
-
-#include "NotifyArgs.h"
-#include "TestConstants.h"
-
-namespace android {
-
-MATCHER_P(WithSource, source, "InputEvent with specified source") {
-    *result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
-                     << inputEventSourceToString(arg.source);
-    return arg.source == source;
-}
-
-/// Key action
-class WithKeyActionMatcher {
-public:
-    using is_gtest_matcher = void;
-    explicit WithKeyActionMatcher(int32_t action) : mAction(action) {}
-
-    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
-        return mAction == args.action;
-    }
-
-    bool MatchAndExplain(const KeyEvent& event, std::ostream*) const {
-        return mAction == event.getAction();
-    }
-
-    void DescribeTo(std::ostream* os) const {
-        *os << "with key action " << KeyEvent::actionToString(mAction);
-    }
-
-    void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
-
-private:
-    const int32_t mAction;
-};
-
-WithKeyActionMatcher WithKeyAction(int32_t action);
-
-/// Motion action
-class WithMotionActionMatcher {
-public:
-    using is_gtest_matcher = void;
-    explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
-
-    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
-        bool matches = mAction == args.action;
-        if (args.action == AMOTION_EVENT_ACTION_CANCEL) {
-            matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0;
-        }
-        return matches;
-    }
-
-    bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
-        bool matches = mAction == event.getAction();
-        if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
-            matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
-        }
-        return matches;
-    }
-
-    void DescribeTo(std::ostream* os) const {
-        *os << "with motion action " << MotionEvent::actionToString(mAction);
-        if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
-            *os << " and FLAG_CANCELED";
-        }
-    }
-
-    void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
-
-private:
-    const int32_t mAction;
-};
-
-WithMotionActionMatcher WithMotionAction(int32_t action);
-
-/// Display Id
-class WithDisplayIdMatcher {
-public:
-    using is_gtest_matcher = void;
-    explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
-
-    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
-        return mDisplayId == args.displayId;
-    }
-
-    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
-        return mDisplayId == args.displayId;
-    }
-
-    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
-        return mDisplayId == event.getDisplayId();
-    }
-
-    void DescribeTo(std::ostream* os) const { *os << "with display id " << mDisplayId; }
-
-    void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
-
-private:
-    const int32_t mDisplayId;
-};
-
-WithDisplayIdMatcher WithDisplayId(int32_t displayId);
-
-/// Device Id
-class WithDeviceIdMatcher {
-public:
-    using is_gtest_matcher = void;
-    explicit WithDeviceIdMatcher(int32_t deviceId) : mDeviceId(deviceId) {}
-
-    bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
-        return mDeviceId == args.deviceId;
-    }
-
-    bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const {
-        return mDeviceId == args.deviceId;
-    }
-
-    bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const {
-        return mDeviceId == args.deviceId;
-    }
-
-    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
-        return mDeviceId == event.getDeviceId();
-    }
-
-    void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
-
-    void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
-
-private:
-    const int32_t mDeviceId;
-};
-
-WithDeviceIdMatcher WithDeviceId(int32_t deviceId);
-
-MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") {
-    *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode;
-    return arg.keyCode == keyCode;
-}
-
-MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") {
-    return arg.getRepeatCount() == repeatCount;
-}
-
-MATCHER_P(WithPointerCount, count, "MotionEvent with specified number of pointers") {
-    *result_listener << "expected " << count << " pointer(s), but got " << arg.getPointerCount();
-    return arg.getPointerCount() == count;
-}
-
-MATCHER_P2(WithPointerId, index, id, "MotionEvent with specified pointer ID for pointer index") {
-    const auto argPointerId = arg.pointerProperties[index].id;
-    *result_listener << "expected pointer with index " << index << " to have ID " << argPointerId;
-    return argPointerId == id;
-}
-
-MATCHER_P2(WithCoords, x, y, "InputEvent with specified coords") {
-    const auto argX = arg.pointerCoords[0].getX();
-    const auto argY = arg.pointerCoords[0].getY();
-    *result_listener << "expected coords (" << x << ", " << y << "), but got (" << argX << ", "
-                     << argY << ")";
-    return argX == x && argY == y;
-}
-
-MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position") {
-    const auto argX = arg.xCursorPosition;
-    const auto argY = arg.yCursorPosition;
-    *result_listener << "expected cursor position (" << x << ", " << y << "), but got (" << argX
-                     << ", " << argY << ")";
-    return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
-}
-
-MATCHER_P3(WithPointerCoords, pointer, x, y, "InputEvent with specified coords for pointer") {
-    const auto argX = arg.pointerCoords[pointer].getX();
-    const auto argY = arg.pointerCoords[pointer].getY();
-    *result_listener << "expected pointer " << pointer << " to have coords (" << x << ", " << y
-                     << "), but got (" << argX << ", " << argY << ")";
-    return argX == x && argY == y;
-}
-
-MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
-    const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-    const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-    *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
-                     << ", " << argY << ")";
-    return argX == x && argY == y;
-}
-
-MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
-           "InputEvent with specified touchpad gesture offset") {
-    const auto argGestureX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET);
-    const auto argGestureY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET);
-    const double xDiff = fabs(argGestureX - dx);
-    const double yDiff = fabs(argGestureY - dy);
-    *result_listener << "expected gesture offset (" << dx << ", " << dy << ") within " << epsilon
-                     << ", but got (" << argGestureX << ", " << argGestureY << ")";
-    return xDiff <= epsilon && yDiff <= epsilon;
-}
-
-MATCHER_P3(WithGestureScrollDistance, x, y, epsilon,
-           "InputEvent with specified touchpad gesture scroll distance") {
-    const auto argXDistance =
-            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE);
-    const auto argYDistance =
-            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE);
-    const double xDiff = fabs(argXDistance - x);
-    const double yDiff = fabs(argYDistance - y);
-    *result_listener << "expected gesture offset (" << x << ", " << y << ") within " << epsilon
-                     << ", but got (" << argXDistance << ", " << argYDistance << ")";
-    return xDiff <= epsilon && yDiff <= epsilon;
-}
-
-MATCHER_P2(WithGesturePinchScaleFactor, factor, epsilon,
-           "InputEvent with specified touchpad pinch gesture scale factor") {
-    const auto argScaleFactor =
-            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR);
-    *result_listener << "expected gesture scale factor " << factor << " within " << epsilon
-                     << " but got " << argScaleFactor;
-    return fabs(argScaleFactor - factor) <= epsilon;
-}
-
-MATCHER_P(WithGestureSwipeFingerCount, count,
-          "InputEvent with specified touchpad swipe finger count") {
-    const auto argFingerCount =
-            arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT);
-    *result_listener << "expected gesture swipe finger count " << count << " but got "
-                     << argFingerCount;
-    return fabs(argFingerCount - count) <= EPSILON;
-}
-
-MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
-    const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-    *result_listener << "expected pressure " << pressure << ", but got " << argPressure;
-    return argPressure == pressure;
-}
-
-MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
-    const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
-    const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-    *result_listener << "expected touch dimensions " << maj << " major x " << min
-                     << " minor, but got " << argMajor << " major x " << argMinor << " minor";
-    return argMajor == maj && argMinor == min;
-}
-
-MATCHER_P2(WithToolDimensions, maj, min, "InputEvent with specified tool dimensions") {
-    const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR);
-    const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR);
-    *result_listener << "expected tool dimensions " << maj << " major x " << min
-                     << " minor, but got " << argMajor << " major x " << argMinor << " minor";
-    return argMajor == maj && argMinor == min;
-}
-
-MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") {
-    const auto argToolType = arg.pointerProperties[0].toolType;
-    *result_listener << "expected tool type " << ftl::enum_string(toolType) << ", but got "
-                     << ftl::enum_string(argToolType);
-    return argToolType == toolType;
-}
-
-MATCHER_P2(WithPointerToolType, pointer, toolType,
-           "InputEvent with specified tool type for pointer") {
-    const auto argToolType = arg.pointerProperties[pointer].toolType;
-    *result_listener << "expected pointer " << pointer << " to have tool type "
-                     << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
-    return argToolType == toolType;
-}
-
-MATCHER_P(WithFlags, flags, "InputEvent with specified flags") {
-    *result_listener << "expected flags " << flags << ", but got " << arg.flags;
-    return arg.flags == static_cast<int32_t>(flags);
-}
-
-MATCHER_P(WithMotionClassification, classification,
-          "InputEvent with specified MotionClassification") {
-    *result_listener << "expected classification " << motionClassificationToString(classification)
-                     << ", but got " << motionClassificationToString(arg.classification);
-    return arg.classification == classification;
-}
-
-MATCHER_P(WithButtonState, buttons, "InputEvent with specified button state") {
-    *result_listener << "expected button state " << buttons << ", but got " << arg.buttonState;
-    return arg.buttonState == buttons;
-}
-
-MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
-    *result_listener << "expected action button " << actionButton << ", but got "
-                     << arg.actionButton;
-    return arg.actionButton == actionButton;
-}
-
-MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
-    *result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
-    return arg.eventTime == eventTime;
-}
-
-MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
-    *result_listener << "expected down time " << downTime << ", but got " << arg.downTime;
-    return arg.downTime == downTime;
-}
-
-MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") {
-    *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision
-                     << ", but got " << arg.xPrecision << " and " << arg.yPrecision;
-    return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
-}
-
-} // namespace android
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 02abf9f..6203a1d 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -23,7 +23,7 @@
 #include "FakePointerController.h"
 #include "InputMapperTest.h"
 #include "InterfaceMocks.h"
-#include "TestInputListenerMatchers.h"
+#include "TestEventMatchers.h"
 
 #define TAG "TouchpadInputMapper_test"
 
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 7cfcf71..78f7291 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -23,8 +23,8 @@
 #include <thread>
 #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 
+#include "TestEventMatchers.h"
 #include "TestInputListener.h"
-#include "TestInputListenerMatchers.h"
 
 using ::testing::AllOf;
 
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 72780fb..6daeaaf 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -15,6 +15,9 @@
  */
 
 #include <fuzzer/FuzzedDataProvider.h>
+#include <linux/input.h>
+
+#include "../../InputDeviceMetricsSource.h"
 #include "dispatcher/LatencyTracker.h"
 
 namespace android {
@@ -65,7 +68,11 @@
                     int32_t isDown = fdp.ConsumeBool();
                     nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
                     nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
-                    tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+                    const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
+                    std::set<InputDeviceUsageSource> sources = {
+                            fdp.ConsumeEnum<InputDeviceUsageSource>()};
+                    tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId,
+                                          sources);
                 },
                 [&]() -> void {
                     int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 71c75f9..9d0f285 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -31,9 +31,6 @@
         "-Wconversion",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
-    static_libs: [
-        "libsurfaceflingerflags",
-    ],
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2dc7332..370e4b6 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -63,6 +63,7 @@
 cc_library {
     name: "libcompositionengine",
     defaults: ["libcompositionengine_defaults"],
+    static_libs: ["libsurfaceflingerflags"],
     srcs: [
         "src/planner/CachedSet.cpp",
         "src/planner/Flattener.cpp",
@@ -107,6 +108,7 @@
         "libgtest",
         "libgmock",
         "libcompositionengine",
+        "libsurfaceflingerflags_test",
     ],
     local_include_dirs: ["include"],
     export_include_dirs: ["include"],
@@ -141,6 +143,7 @@
         "librenderengine_mocks",
         "libgmock",
         "libgtest",
+        "libsurfaceflingerflags_test",
     ],
     // For some reason, libvulkan isn't picked up from librenderengine
     // Probably ASAN related?
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 35ca3a5..11759b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -26,6 +26,7 @@
 #include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/ShadowSettings.h>
 #include <ui/Transform.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -132,8 +133,7 @@
     // The bounds of the layer in layer local coordinates
     FloatRect geomLayerBounds;
 
-    // length of the shadow in screen space
-    float shadowRadius{0.f};
+    ShadowSettings shadowSettings;
 
     // List of regions that require blur
     std::vector<BlurRegion> blurRegions;
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 97725ea..f339d41 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -392,6 +392,10 @@
     dumpVal(out, "dv", hasDolbyVisionSupport());
     dumpVal(out, "metadata", getSupportedPerFrameMetadata());
 
+    out.append("\n   Hdr Luminance Info:");
+    dumpVal(out, "desiredMinLuminance", mHdrCapabilities.getDesiredMinLuminance());
+    dumpVal(out, "desiredMaxLuminance", mHdrCapabilities.getDesiredMaxLuminance());
+    dumpVal(out, "desiredMaxAverageLuminance", mHdrCapabilities.getDesiredMaxAverageLuminance());
     out.append("\n");
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 426cc57..2d10866 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -68,7 +68,7 @@
     dumpVal(out, "geomLayerBounds", geomLayerBounds);
 
     out.append("      ");
-    dumpVal(out, "shadowRadius", shadowRadius);
+    dumpVal(out, "shadowLength", shadowSettings.length);
 
     out.append("\n      ");
     dumpVal(out, "blend", toString(blendMode), blendMode);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 78c23da..fa3733b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -588,10 +588,10 @@
     const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
     visibleRegion.set(visibleRect);
 
-    if (layerFEState->shadowRadius > 0.0f) {
+    if (layerFEState->shadowSettings.length > 0.0f) {
         // if the layer casts a shadow, offset the layers visible region and
         // calculate the shadow region.
-        const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
+        const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowSettings.length) * -1.0f);
         Rect visibleRectWithShadows(visibleRect);
         visibleRectWithShadows.inset(inset, inset, inset, inset);
         visibleRegion.set(visibleRectWithShadows);
@@ -967,7 +967,7 @@
             case ui::Dataspace::BT2020_ITU_HLG:
                 bestDataSpace = ui::Dataspace::DISPLAY_P3;
                 // When there's mixed PQ content and HLG content, we set the HDR
-                // data space to be BT2020_PQ and convert HLG to PQ.
+                // data space to be BT2020_HLG and convert PQ to HLG.
                 if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) {
                     *outHdrDataSpace = ui::Dataspace::BT2020_HLG;
                 }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index fe56969..7fe3369 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -222,8 +222,8 @@
 
     // Some HWCs may clip client composited input to its displayFrame. Make sure
     // that this does not cut off the shadow.
-    if (layerState.forceClientComposition && layerState.shadowRadius > 0.0f) {
-        const auto outset = layerState.shadowRadius;
+    if (layerState.forceClientComposition && layerState.shadowSettings.length > 0.0f) {
+        const auto outset = layerState.shadowSettings.length;
         geomLayerBounds.left -= outset;
         geomLayerBounds.top -= outset;
         geomLayerBounds.right += outset;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 630906a..1c54469 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -334,7 +334,7 @@
 
 TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame) {
     const int kShadowRadius = 5;
-    mLayerFEState.shadowRadius = kShadowRadius;
+    mLayerFEState.shadowSettings.length = kShadowRadius;
     mLayerFEState.forceClientComposition = true;
 
     mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
@@ -345,7 +345,7 @@
 
 TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame_onlyIfForcingClientComposition) {
     const int kShadowRadius = 5;
-    mLayerFEState.shadowRadius = kShadowRadius;
+    mLayerFEState.shadowSettings.length = kShadowRadius;
     mLayerFEState.forceClientComposition = false;
 
     mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee6998a..cdcb68d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1830,7 +1830,7 @@
     ui::Transform translate;
     translate.set(50, 50);
     mLayer.layerFEState.geomLayerTransform = translate;
-    mLayer.layerFEState.shadowRadius = 10.0f;
+    mLayer.layerFEState.shadowSettings.length = 10.0f;
 
     mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
     // half of the layer including the casting shadow is covered and opaque
@@ -1872,7 +1872,7 @@
     ui::Transform translate;
     translate.set(50, 50);
     mLayer.layerFEState.geomLayerTransform = translate;
-    mLayer.layerFEState.shadowRadius = 10.0f;
+    mLayer.layerFEState.shadowSettings.length = 10.0f;
 
     mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
     // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
@@ -1897,7 +1897,7 @@
     ui::Transform translate;
     translate.set(50, 50);
     mLayer.layerFEState.geomLayerTransform = translate;
-    mLayer.layerFEState.shadowRadius = 10.0f;
+    mLayer.layerFEState.shadowSettings.length = 10.0f;
 
     mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
     // Casting layer and its shadows are covered by an opaque region
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index f9c8e81..7a85da0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -382,7 +382,6 @@
         sidebandStream = requested.sidebandStream;
     }
     if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
-        shadowRadius = requested.shadowRadius;
         shadowSettings.length = requested.shadowRadius;
     }
     if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index a1c72a9..80a51ea 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -69,7 +69,6 @@
     RoundedCornerState roundedCorner;
     FloatRect transformedBounds;
     Rect transformedBoundsWithoutTransparentRegion;
-    renderengine::ShadowSettings shadowSettings;
     bool premultipliedAlpha;
     ui::Transform parentTransform;
     Rect bufferSize;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 55be398..03c0993 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -353,7 +353,7 @@
     snapshot.isSecure = false;
     snapshot.color.a = 1.0_hf;
     snapshot.colorTransformIsIdentity = true;
-    snapshot.shadowRadius = 0.f;
+    snapshot.shadowSettings.length = 0.f;
     snapshot.layerMetadata.mMap.clear();
     snapshot.relativeLayerMetadata.mMap.clear();
     snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
@@ -990,9 +990,12 @@
 }
 
 void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
-                                         const renderengine::ShadowSettings& globalShadowSettings) {
-    if (snapshot.shadowRadius > 0.f) {
-        snapshot.shadowSettings = globalShadowSettings;
+                                         const ShadowSettings& globalShadowSettings) {
+    if (snapshot.shadowSettings.length > 0.f) {
+        snapshot.shadowSettings.ambientColor = globalShadowSettings.ambientColor;
+        snapshot.shadowSettings.spotColor = globalShadowSettings.spotColor;
+        snapshot.shadowSettings.lightPos = globalShadowSettings.lightPos;
+        snapshot.shadowSettings.lightRadius = globalShadowSettings.lightRadius;
 
         // Note: this preserves existing behavior of shadowing the entire layer and not cropping
         // it if transparent regions are present. This may not be necessary since shadows are
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3d64b36..1506913 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -47,7 +47,7 @@
         const DisplayInfos& displays;
         // Set to true if there were display changes since last update.
         bool displayChanges = false;
-        const renderengine::ShadowSettings& globalShadowSettings;
+        const ShadowSettings& globalShadowSettings;
         bool supportsBlur = true;
         bool forceFullDamage = false;
         std::optional<FloatRect> parentCrop = std::nullopt;
@@ -108,7 +108,7 @@
     void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
                            const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
     static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
-                              const renderengine::ShadowSettings& globalShadowSettings);
+                              const ShadowSettings& globalShadowSettings);
     void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
                      const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
                      const Args& args);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5ae2999..2dc8758 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -572,7 +572,7 @@
     snapshot->outputFilter = getOutputFilter();
     snapshot->isVisible = isVisible();
     snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
-    snapshot->shadowRadius = mEffectiveShadowRadius;
+    snapshot->shadowSettings.length = mEffectiveShadowRadius;
 
     snapshot->contentDirty = contentDirty;
     contentDirty = false;
@@ -1267,7 +1267,8 @@
         return parentFrameRate;
     }();
 
-    *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
+    auto now = systemTime();
+    *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
 
     // The frame rate is propagated to the children
     bool childrenHaveFrameRate = false;
@@ -1283,7 +1284,8 @@
     // layer as NoVote to allow the children to control the refresh rate
     if (!frameRate.isValid() && childrenHaveFrameRate) {
         *transactionNeeded |=
-                setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+                setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
+                                               now);
     }
 
     // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
@@ -1492,7 +1494,7 @@
     addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
 }
 
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
     if (mDrawingState.frameRateForLayerTree == frameRate) {
         return false;
     }
@@ -1506,19 +1508,20 @@
     setTransactionFlags(eTransactionNeeded);
 
     mFlinger->mScheduler
-            ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+            ->recordLayerHistory(sequence, getLayerProps(), now, now,
                                  scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
     return true;
 }
 
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
+                                     nsecs_t now) {
     if (mDrawingState.frameRateForLayerTree == frameRate) {
         return false;
     }
 
     mDrawingState.frameRateForLayerTree = frameRate;
     mFlinger->mScheduler
-            ->recordLayerHistory(sequence, layerProps, systemTime(),
+            ->recordLayerHistory(sequence, layerProps, now, now,
                                  scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
     return true;
 }
@@ -3225,7 +3228,7 @@
                                       mOwnerUid, postTime, getGameMode());
 
     if (mFlinger->mLegacyFrontEndEnabled) {
-        recordLayerHistoryBufferUpdate(getLayerProps());
+        recordLayerHistoryBufferUpdate(getLayerProps(), systemTime());
     }
 
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -3256,7 +3259,7 @@
     mDrawingState.isAutoTimestamp = isAutoTimestamp;
 }
 
-void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) {
     ATRACE_CALL();
     const nsecs_t presentTime = [&] {
         if (!mDrawingState.isAutoTimestamp) {
@@ -3310,14 +3313,14 @@
         ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
     }
 
-    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
                                              scheduler::LayerHistory::LayerUpdateType::Buffer);
 }
 
-void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps, nsecs_t now) {
     const nsecs_t presentTime =
             mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
-    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
                                              scheduler::LayerHistory::LayerUpdateType::AnimationTX);
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 78a3a7c..0b66866 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -908,10 +908,10 @@
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                    const sp<GraphicBuffer>& buffer, uint64_t framenumber,
                                    const sp<Fence>& releaseFence);
-    bool setFrameRateForLayerTreeLegacy(FrameRate);
-    bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
-    void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
-    void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+    bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now);
+    bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now);
+    void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
+    void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
     auto getLayerProps() const {
         return scheduler::LayerProps{
                 .visible = isVisible(),
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 97c4145..48a9190 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -310,7 +310,7 @@
 
 void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
                                              const Rect& layerStackRect) const {
-    renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+    ShadowSettings state = mSnapshot->shadowSettings;
     if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
         return;
     }
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 144e1f5..aa6026e 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -402,7 +402,7 @@
                                    [&]() { return layerInfo->mutable_screen_bounds(); });
     LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
                                    [&]() { return layerInfo->mutable_corner_radius_crop(); });
-    layerInfo->set_shadow_radius(snapshot.shadowRadius);
+    layerInfo->set_shadow_radius(snapshot.shadowSettings.length);
 
     layerInfo->set_id(snapshot.uniqueSequence);
     layerInfo->set_original_id(snapshot.sequence);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 40bda83..bac1ec6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -91,6 +91,7 @@
 
 private:
     friend class LayerHistoryTest;
+    friend class LayerHistoryIntegrationTest;
     friend class TestableScheduler;
 
     using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 6286b28..d580b58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -62,6 +62,7 @@
     static constexpr size_t kNumSmallDirtyThreshold = 2;
 
     friend class LayerHistoryTest;
+    friend class LayerHistoryIntegrationTest;
     friend class LayerInfoTest;
 
 public:
@@ -264,6 +265,7 @@
 
     private:
         friend class LayerHistoryTest;
+        friend class LayerHistoryIntegrationTest;
 
         // Holds the refresh rate when it was calculated
         struct RefreshRateData {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 76f1af9..5115c42 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -192,7 +192,8 @@
     for (const auto& [id, display] : mDisplays) {
         if (id == pacesetterId) continue;
 
-        const FrameTargeter& targeter = *display.targeterPtr;
+        FrameTargeter& targeter = *display.targeterPtr;
+        targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
         targets.try_emplace(id, &targeter.target());
     }
 
@@ -206,8 +207,6 @@
         if (id == pacesetterId) continue;
 
         FrameTargeter& targeter = *display.targeterPtr;
-        targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
-
         targeters.try_emplace(id, &targeter);
     }
 
@@ -625,9 +624,9 @@
 }
 
 void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
-                                   LayerHistory::LayerUpdateType updateType) {
+                                   nsecs_t now, LayerHistory::LayerUpdateType updateType) {
     if (pacesetterSelectorPtr()->canSwitch()) {
-        mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
+        mLayerHistory.record(id, layerProps, presentTime, now, updateType);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index e6db654..3441318 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -230,7 +230,7 @@
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
     void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
-                            LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
+                            nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
     void deregisterLayer(Layer*);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 1f922f1..c4c9fa5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -28,10 +28,13 @@
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncTracker.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 #undef LOG_TAG
 #define LOG_TAG "VSyncDispatch"
 
 namespace android::scheduler {
+using namespace com::android::graphics::surfaceflinger;
 
 using base::StringAppendF;
 
@@ -100,8 +103,14 @@
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
     bool const wouldSkipAWakeup =
             mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
-    if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
-        return getExpectedCallbackTime(nextVsyncTime, timing);
+    if (flags::dont_skip_on_early()) {
+        if (wouldSkipAVsyncTarget || wouldSkipAWakeup) {
+            return getExpectedCallbackTime(mArmedInfo->mActualVsyncTime, timing);
+        }
+    } else {
+        if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
+            return getExpectedCallbackTime(nextVsyncTime, timing);
+        }
     }
 
     nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4d8dc94..7e799bb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2203,44 +2203,46 @@
     return mustComposite;
 }
 
-void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
-    using Changes = frontend::RequestedLayerState::Changes;
-    if (snapshot.path.isClone()) {
-        return;
-    }
+void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
+    for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+        using Changes = frontend::RequestedLayerState::Changes;
+        if (snapshot->path.isClone()) {
+            continue;
+        }
 
-    if (!snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
-        (snapshot.clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
-        return;
-    }
+        if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
+            (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+            continue;
+        }
 
-    const auto layerProps = scheduler::LayerProps{
-            .visible = snapshot.isVisible,
-            .bounds = snapshot.geomLayerBounds,
-            .transform = snapshot.geomLayerTransform,
-            .setFrameRateVote = snapshot.frameRate,
-            .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
-    };
+        const auto layerProps = scheduler::LayerProps{
+                .visible = snapshot->isVisible,
+                .bounds = snapshot->geomLayerBounds,
+                .transform = snapshot->geomLayerTransform,
+                .setFrameRateVote = snapshot->frameRate,
+                .frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+        };
 
-    auto it = mLegacyLayers.find(snapshot.sequence);
-    LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
-                        snapshot.getDebugString().c_str());
+        auto it = mLegacyLayers.find(snapshot->sequence);
+        LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+                            snapshot->getDebugString().c_str());
 
-    if (snapshot.changes.test(Changes::Animation)) {
-        it->second->recordLayerHistoryAnimationTx(layerProps);
-    }
+        if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+            mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
+                                                         snapshot->defaultFrameRateCompatibility);
+        }
 
-    if (snapshot.clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
-        mScheduler->setDefaultFrameRateCompatibility(snapshot.sequence,
-                                                     snapshot.defaultFrameRateCompatibility);
-    }
+        if (snapshot->changes.test(Changes::Animation)) {
+            it->second->recordLayerHistoryAnimationTx(layerProps, now);
+        }
 
-    if (snapshot.changes.test(Changes::FrameRate)) {
-        it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
-    }
+        if (snapshot->changes.test(Changes::FrameRate)) {
+            it->second->setFrameRateForLayerTree(snapshot->frameRate, layerProps, now);
+        }
 
-    if (snapshot.changes.test(Changes::Buffer)) {
-        it->second->recordLayerHistoryBufferUpdate(layerProps);
+        if (snapshot->changes.test(Changes::Buffer)) {
+            it->second->recordLayerHistoryBufferUpdate(layerProps, now);
+        }
     }
 }
 
@@ -2379,8 +2381,8 @@
             mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
         }
 
+        updateLayerHistory(latchTime);
         mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
-            updateLayerHistory(snapshot);
             if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) ==
                 mLayersIdsWithQueuedFrames.end())
                 return;
@@ -4025,7 +4027,7 @@
 
     if (sysprop::use_content_detection_for_refresh_rate(false)) {
         features |= Feature::kContentDetection;
-        if (base::GetBoolProperty("debug.sf.enable_small_dirty_detection"s, false)) {
+        if (flags::vrr_small_dirty_detection()) {
             features |= Feature::kSmallDirtyContentDetection;
         }
     }
@@ -4832,7 +4834,7 @@
     for (const auto& listener : listenerCallbacks) {
         mTransactionCallbackInvoker.addEmptyTransaction(listener);
     }
-
+    nsecs_t now = systemTime();
     uint32_t clientStateFlags = 0;
     for (auto& resolvedState : states) {
         if (mLegacyFrontEndEnabled) {
@@ -4854,7 +4856,7 @@
                         .setFrameRateVote = layer->getFrameRateForLayerTree(),
                         .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
                 };
-                layer->recordLayerHistoryAnimationTx(layerProps);
+                layer->recordLayerHistoryAnimationTx(layerProps, now);
             }
         }
     }
@@ -6444,6 +6446,8 @@
                   mMisc2FlagEarlyBootValue == mMisc2FlagLateBootValue ? "stable" : "modified");
     StringAppendF(&result, "VrrConfigFlagValue: %s\n",
                   flagutils::vrrConfigEnabled() ? "true" : "false");
+    StringAppendF(&result, "DontSkipOnEarlyFlagValue: %s\n",
+                  flags::dont_skip_on_early() ? "true" : "false");
 
     getRenderEngine().dump(result);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a5a2341..cbea312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -419,7 +419,7 @@
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
 
-        renderengine::ShadowSettings globalShadowSettings;
+        ShadowSettings globalShadowSettings;
 
         void traverse(const LayerVector::Visitor& visitor) const;
         void traverseInZOrder(const LayerVector::Visitor& visitor) const;
@@ -732,7 +732,7 @@
                                     bool& out) REQUIRES(kMainThreadContext);
     bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
                               bool& out) REQUIRES(kMainThreadContext);
-    void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
+    void updateLayerHistory(nsecs_t now);
     frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
 
     void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 6f53d62..922fd07 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -36,6 +36,9 @@
   "hwasan-presubmit": [
     {
       "name": "libscheduler_test"
+    },
+    {
+      "name": "libsurfaceflinger_unittest"
     }
   ]
 }
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 23fe8fa..c2d1954 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -56,7 +56,7 @@
     frontend::LayerSnapshotBuilder snapshotBuilder;
     ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
 
-    renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+    ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
     char value[PROPERTY_VALUE_MAX];
     property_get("ro.surface_flinger.supports_background_blur", value, "0");
     bool supportsBlur = atoi(value);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 2f5a360..a8727f9 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -182,8 +182,9 @@
     uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
     uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
     uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
-    scheduler::VSyncPredictor tracker{kDisplayId, mFdp.ConsumeIntegral<uint16_t>() /*period*/,
-                                      historySize, minimumSamplesForPrediction,
+    nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX);
+    scheduler::VSyncPredictor tracker{kDisplayId, idealPeriod, historySize,
+                                      minimumSamplesForPrediction,
                                       mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
     uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
     tracker.setPeriod(period);
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index d4ab786..5a277bd 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -12,7 +12,7 @@
   name: "connected_display"
   namespace: "core_graphics"
   description: "Controls SurfaceFlinger support for Connected Displays in 24Q1"
-  bug: "299486625"
+  bug: "278199093"
   is_fixed_read_only: true
 }
 
@@ -30,3 +30,26 @@
   bug: "284845445"
   is_fixed_read_only: true
 }
+
+flag {
+  name: "vrr_small_dirty_detection"
+  namespace: "core_graphics"
+  description: "Controls small dirty detection for VRR"
+  bug: "283055450"
+  is_fixed_read_only: true
+}
+
+flag {
+  name: "dont_skip_on_early"
+  namespace: "core_graphics"
+  description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
+  bug: "273702768"
+}
+
+flag {
+  name: "multithreaded_present"
+  namespace: "core_graphics"
+  description: "Controls whether to offload present calls to another thread"
+  bug: "259132483"
+  is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 5cc3154..5888a55 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -66,6 +66,7 @@
     static_libs: [
         "liblayers_proto",
         "android.hardware.graphics.composer@2.1",
+        "libsurfaceflingerflags",
     ],
     shared_libs: [
         "android.hardware.graphics.common@1.2",
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 2fcb9e0..3c09422 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -126,7 +126,7 @@
         << info.touchableRegionBounds.right << "," << info.touchableRegionBounds.bottom << "}";
 }
 
-struct find_id : std::unary_function<LayerInfo, bool> {
+struct find_id {
     uint64_t id;
     find_id(uint64_t id) : id(id) {}
     bool operator()(LayerInfo const& m) const { return m.id == id; }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f4516c7..91910c7 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,6 +42,13 @@
     ],
 }
 
+cc_aconfig_library {
+    name: "libsurfaceflingerflags_test",
+    aconfig_declarations: "surfaceflinger_flags",
+    // TODO(b/304338314): uncomment the below line once the bug is fixed
+    // test: true,
+}
+
 cc_test {
     name: "libsurfaceflinger_unittest",
     defaults: [
@@ -50,22 +57,6 @@
         "surfaceflinger_defaults",
     ],
     test_suites: ["device-tests"],
-    sanitize: {
-        // Using the address sanitizer not only helps uncover issues in the code
-        // covered by the tests, but also covers some of the tricky injection of
-        // fakes the unit tests currently do.
-        //
-        // Note: If you get an runtime link error like:
-        //
-        //   CANNOT LINK EXECUTABLE "/data/local/tmp/libsurfaceflinger_unittest": library "libclang_rt.asan-aarch64-android.so" not found
-        //
-        // it is because the address sanitizer shared objects are not installed
-        // by default in the system image.
-        //
-        // You can either "make dist tests" before flashing, or set this
-        // option to false temporarily.
-        address: true,
-    },
     static_libs: ["libc++fs"],
     srcs: [
         ":libsurfaceflinger_mock_sources",
@@ -93,6 +84,7 @@
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
+        "LayerHistoryIntegrationTest.cpp",
         "LayerInfoTest.cpp",
         "LayerMetadataTest.cpp",
         "LayerHierarchyTest.cpp",
@@ -183,6 +175,7 @@
         "libtimestats_proto",
         "libtonemap",
         "perfetto_trace_protos",
+        "libsurfaceflingerflags_test",
     ],
     shared_libs: [
         "android.hardware.configstore-utils",
diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/tests/unittests/FlagUtils.h
new file mode 100644
index 0000000..7103684
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FlagUtils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
+
+namespace android {
+class TestFlagSetter {
+public:
+    TestFlagSetter(bool (*getter)(), void((*setter)(bool)), bool flagValue) {
+        const bool initialValue = getter();
+        setter(flagValue);
+        mResetFlagValue = [=] { setter(initialValue); };
+    }
+
+    ~TestFlagSetter() { mResetFlagValue(); }
+
+private:
+    std::function<void()> mResetFlagValue;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index d7ac038..d319dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -18,11 +18,14 @@
 #include <gtest/gtest.h>
 
 #include <gui/fake/BufferData.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/ShadowSettings.h>
 
 #include "Client.h" // temporarily needed for LayerCreationArgs
 #include "FrontEnd/LayerCreationArgs.h"
 #include "FrontEnd/LayerHierarchy.h"
 #include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
 
 namespace android::surfaceflinger::frontend {
 
@@ -358,6 +361,19 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what =
+                layer_state_t::eDefaultFrameRateCompatibilityChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.defaultFrameRateCompatibility =
+                defaultFrameRateCompatibility;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setRoundedCorners(uint32_t id, float radius) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -384,6 +400,16 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setBuffer(uint32_t id) {
+        static uint64_t sBufferId = 1;
+        setBuffer(id,
+                  std::make_shared<renderengine::mock::
+                                           FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                                sBufferId++,
+                                                                HAL_PIXEL_FORMAT_RGBA_8888,
+                                                                GRALLOC_USAGE_PROTECTED /*usage*/));
+    }
+
     void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -406,7 +432,64 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setShadowRadius(uint32_t id, float shadowRadius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.shadowRadius = shadowRadius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     LayerLifecycleManager mLifecycleManager;
 };
 
+class LayerSnapshotTestBase : public LayerHierarchyTestBase {
+protected:
+    LayerSnapshotTestBase() : LayerHierarchyTestBase() {}
+
+    void createRootLayer(uint32_t id) override {
+        LayerHierarchyTestBase::createRootLayer(id);
+        setColor(id);
+    }
+
+    void createLayer(uint32_t id, uint32_t parentId) override {
+        LayerHierarchyTestBase::createLayer(id, parentId);
+        setColor(parentId);
+    }
+
+    void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
+        LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
+        setColor(id);
+    }
+
+    void update(LayerSnapshotBuilder& snapshotBuilder) {
+        if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+            mHierarchyBuilder.update(mLifecycleManager.getLayers(),
+                                     mLifecycleManager.getDestroyedLayers());
+        }
+        LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+                                        .layerLifecycleManager = mLifecycleManager,
+                                        .includeMetadata = false,
+                                        .displays = mFrontEndDisplayInfos,
+                                        .displayChanges = mHasDisplayChanges,
+                                        .globalShadowSettings = globalShadowSettings,
+                                        .supportsBlur = true,
+                                        .supportedLayerGenericMetadata = {},
+                                        .genericLayerMetadataKeyMap = {}};
+        snapshotBuilder.update(args);
+
+        mLifecycleManager.commitChanges();
+    }
+
+    LayerHierarchyBuilder mHierarchyBuilder{{}};
+
+    DisplayInfos mFrontEndDisplayInfos;
+    bool mHasDisplayChanges = false;
+
+    ShadowSettings globalShadowSettings;
+};
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
new file mode 100644
index 0000000..a462082
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -0,0 +1,873 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryIntegrationTest"
+
+#include <Layer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <renderengine/mock/FakeExternalTexture.h>
+
+#include "FpsOps.h"
+#include "LayerHierarchyTest.h"
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockSchedulerCallback.h"
+
+namespace android::scheduler {
+
+using android::mock::createDisplayMode;
+
+class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
+protected:
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
+    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
+    static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+
+    static constexpr Fps LO_FPS = 30_Hz;
+    static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
+
+    static constexpr Fps HI_FPS = 90_Hz;
+    static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
+
+    LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
+        mFlinger.resetScheduler(mScheduler);
+        mLifecycleManager = {};
+        mHierarchyBuilder = {{}};
+    }
+
+    void updateLayerSnapshotsAndLayerHistory(nsecs_t now) {
+        LayerSnapshotTestBase::update(mFlinger.mutableLayerSnapshotBuilder());
+        mFlinger.updateLayerHistory(now);
+    }
+
+    void setBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+        uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+        setBuffer(sequence);
+        layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+        updateLayerSnapshotsAndLayerHistory(time);
+    }
+
+    LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+    const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+
+    LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
+        // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+        // however, for testing only, a stable order is required, therefore we sort the list here.
+        // Any tests requiring ordered results must create layers with names.
+        auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
+        std::sort(summary.begin(), summary.end(),
+                  [](const RefreshRateSelector::LayerRequirement& lhs,
+                     const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+                      return lhs.name < rhs.name;
+                  });
+        return summary;
+    }
+
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+        return history().mActiveLayerInfos.size();
+    }
+
+    auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isFrequent(now).isFrequent;
+        });
+    }
+
+    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isAnimating(now);
+        });
+    }
+
+    auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isFrequent(now).clearHistory;
+        });
+    }
+
+    void setDefaultLayerVote(Layer* layer,
+                             LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+        auto [found, layerPair] = history().findLayer(layer->getSequence());
+        if (found != LayerHistory::LayerStatus::NotFound) {
+            layerPair->second->setDefaultLayerVote(vote);
+        }
+    }
+
+    auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+        std::string layerName = "test layer:" + std::to_string(sequence);
+        const auto layer =
+                sp<Layer>::make(LayerCreationArgs{mFlinger.flinger(),
+                                                  nullptr,
+                                                  layerName,
+                                                  0,
+                                                  {},
+                                                  std::make_optional<uint32_t>(sequence)});
+        mFlinger.injectLegacyLayer(layer);
+        createRootLayer(sequence);
+        return layer;
+    }
+
+    auto destroyLayer(sp<Layer>& layer) {
+        uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+        mFlinger.releaseLegacyLayer(sequence);
+        layer.clear();
+        destroyLayerHandle(sequence);
+    }
+
+    void recordFramesAndExpect(sp<Layer>& layer, nsecs_t& time, Fps frameRate,
+                               Fps desiredRefreshRate, int numFrames) {
+        LayerHistory::Summary summary;
+        for (int i = 0; i < numFrames; i++) {
+            setBufferWithPresentTime(layer, time);
+            time += frameRate.getPeriodNsecs();
+
+            summary = summarizeLayerHistory(time);
+        }
+
+        ASSERT_EQ(1u, summary.size());
+        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+        ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
+    }
+
+    std::shared_ptr<RefreshRateSelector> mSelector =
+            std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+                                                                              LO_FPS),
+                                                            createDisplayMode(DisplayModeId(1),
+                                                                              HI_FPS)),
+                                                  DisplayModeId(0));
+
+    mock::SchedulerCallback mSchedulerCallback;
+
+    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+namespace {
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerNoVoteDefaultCompatibility) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    setBuffer(1);
+    setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_NO_VOTE);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerMinVoteDefaultCompatibility) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    setBuffer(1);
+    setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_MIN);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    setBuffer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    // Layer is still considered inactive so we expect to get Min
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+
+    hideLayer(1);
+    setBuffer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
+    createLegacyAndFrontedEndLayer(1);
+    setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote) {
+    createLegacyAndFrontedEndLayer(1);
+    setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
+    createLegacyAndFrontedEndLayer(1);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    // First LayerRequirement is the frame rate specification
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, multipleLayers) {
+    auto layer1 = createLegacyAndFrontedEndLayer(1);
+    auto layer2 = createLegacyAndFrontedEndLayer(2);
+    auto layer3 = createLegacyAndFrontedEndLayer(3);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(3u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    LayerHistory::Summary summary;
+
+    // layer1 is active but infrequent.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer1, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer2 is frequent and has high refresh rate.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer2, time);
+        time += HI_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    // layer1 is still active but infrequent.
+    setBufferWithPresentTime(layer1, time);
+
+    ASSERT_EQ(2u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
+
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer1 is no longer active.
+    // layer2 is frequent and has low refresh rate.
+    for (size_t i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer2, time);
+        time += LO_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 has high refresh rate but not enough history.
+    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        if (i % RATIO == 0) {
+            setBufferWithPresentTime(layer2, time);
+        }
+
+        setBufferWithPresentTime(layer3, time);
+        time += HI_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(2u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer3 becomes recently active.
+    setBufferWithPresentTime(layer3, time);
+    summary = summarizeLayerHistory(time);
+    ASSERT_EQ(2u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer1 expires.
+    destroyLayer(layer1);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    summary = summarizeLayerHistory(time);
+    ASSERT_EQ(2u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+    EXPECT_EQ(2u, layerCount());
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 becomes inactive.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer2, time);
+        time += LO_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 expires.
+    destroyLayer(layer2);
+    updateLayerSnapshotsAndLayerHistory(time);
+    summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer3 becomes active and has high refresh rate.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+        setBufferWithPresentTime(layer3, time);
+        time += HI_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer3 expires.
+    destroyLayer(layer3);
+    updateLayerSnapshotsAndLayerHistory(time);
+    summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(0u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inactiveLayers) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+
+    // the very first updates makes the layer frequent
+    for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+        EXPECT_EQ(1u, layerCount());
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    setBufferWithPresentTime(layer, time);
+
+    EXPECT_EQ(1u, layerCount());
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // advance the time for the previous frame to be inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+    // Now even if we post a quick few frame we should stay infrequent
+    for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+
+        EXPECT_EQ(1u, layerCount());
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        EXPECT_EQ(0, frequentLayerCount(time));
+    }
+
+    // More quick frames will get us to frequent again
+    setBufferWithPresentTime(layer, time);
+    time += HI_FPS_PERIOD;
+
+    EXPECT_EQ(1u, layerCount());
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+    auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+    auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+    hideLayer(2);
+    setFrameRate(1, 60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRate(2, 90.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    setBufferWithPresentTime(explicitVisiblelayer, time);
+    setBufferWithPresentTime(explicitInvisiblelayer, time);
+
+    EXPECT_EQ(2u, layerCount());
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, infrequentAnimatingLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // another update with the same cadence keep in infrequent
+    setBufferWithPresentTime(layer, time);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->changes |=
+            frontend::RequestedLayerState::Changes::Animation;
+    mFlinger.updateLayerHistory(time);
+    // an update as animation will immediately vote for Max
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frequentLayerBecomingInfrequentAndBack) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // Fill up the window with frequent updates
+    for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += (60_Hz).getPeriodNsecs();
+
+        EXPECT_EQ(1u, layerCount());
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // posting a buffer after long inactivity should retain the layer as active
+    time += std::chrono::nanoseconds(3s).count();
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more infrequent buffer should make the layer infrequent
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    setBufferWithPresentTime(layer, time);
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting another buffer should keep the layer infrequent
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more buffers would mean starting of an animation, so making the layer frequent
+    setBufferWithPresentTime(layer, time);
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(1, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting a buffer after long inactivity should retain the layer as active
+    time += std::chrono::nanoseconds(3s).count();
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting another buffer should keep the layer frequent
+    time += (60_Hz).getPeriodNsecs();
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inconclusiveLayerBecomingFrequent) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // Fill up the window with frequent updates
+    for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += (60_Hz).getPeriodNsecs();
+
+        EXPECT_EQ(1u, layerCount());
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // posting infrequent buffers after long inactivity should make the layer
+    // inconclusive but frequent.
+    time += std::chrono::nanoseconds(3s).count();
+    setBufferWithPresentTime(layer, time);
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more buffers should make the layer frequent and switch the refresh rate to max
+    // by clearing the history
+    setBufferWithPresentTime(layer, time);
+    setBufferWithPresentTime(layer, time);
+    setBufferWithPresentTime(layer, time);
+    EXPECT_EQ(1, clearLayerHistoryCount(time));
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, getFramerate) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60Hz) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+        recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+    }
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60_30Hz) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    LayerHistory::Summary summary;
+
+    // layer is active but infrequent.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        auto props = layer->getLayerProps();
+        if (i % 3 == 0) {
+            props.isSmallDirty = false;
+        } else {
+            props.isSmallDirty = true;
+        }
+
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
+}
+
+TEST_F(LayerHistoryIntegrationTest, DISABLED_smallDirtyInMultiLayer) {
+    auto uiLayer = createLegacyAndFrontedEndLayer(1);
+    auto videoLayer = createLegacyAndFrontedEndLayer(2);
+    setFrameRate(2, 30.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(2u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    LayerHistory::Summary summary;
+
+    // uiLayer is updating small dirty.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+        auto props = uiLayer->getLayerProps();
+        props.isSmallDirty = true;
+        setBuffer(1);
+        uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
+        updateLayerSnapshotsAndLayerHistory(time);
+        setBufferWithPresentTime(videoLayer, time);
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+    ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
+}
+
+class LayerHistoryIntegrationTestParameterized
+      : public LayerHistoryIntegrationTest,
+        public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryIntegrationTestParameterized, HeuristicLayerWithInfrequentLayer) {
+    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+    auto heuristicLayer = createLegacyAndFrontedEndLayer(1);
+    auto infrequentLayer = createLegacyAndFrontedEndLayer(2);
+
+    const nsecs_t startTime = systemTime();
+
+    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+    setBufferWithPresentTime(heuristicLayer, startTime);
+    setBufferWithPresentTime(infrequentLayer, startTime);
+
+    nsecs_t time = startTime;
+    nsecs_t lastInfrequentUpdate = startTime;
+    const size_t totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+    size_t infrequentLayerUpdates = 0;
+    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+        time += heuristicUpdateDelta.count();
+        setBufferWithPresentTime(heuristicLayer, time);
+
+        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+            ALOGI("submitting infrequent frame [%zu/%zu]", infrequentLayerUpdates,
+                  totalInfrequentLayerUpdates);
+            lastInfrequentUpdate = time;
+            setBufferWithPresentTime(infrequentLayer, time);
+            infrequentLayerUpdates++;
+        }
+
+        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+            ASSERT_NE(0u, summarizeLayerHistory(time).size());
+            ASSERT_GE(2u, summarizeLayerHistory(time).size());
+
+            bool max = false;
+            bool min = false;
+            Fps heuristic;
+            for (const auto& layer : summarizeLayerHistory(time)) {
+                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+                    heuristic = layer.desiredRefreshRate;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+                    max = true;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+                    min = true;
+                }
+            }
+
+            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+                EXPECT_EQ(24_Hz, heuristic);
+                EXPECT_FALSE(max);
+                if (summarizeLayerHistory(time).size() == 2) {
+                    EXPECT_TRUE(min);
+                }
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
+                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 549a362..33c1d86 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -45,6 +45,8 @@
 
 using android::mock::createDisplayMode;
 
+// WARNING: LEGACY TESTS FOR LEGACY FRONT END
+// Update LayerHistoryIntegrationTest instead
 class LayerHistoryTest : public testing::Test {
 protected:
     static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 69316bf..50dfcaa 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -50,27 +50,12 @@
     --gtest_filter="LayerSnapshotTest.*" --gtest_brief=1
 */
 
-class LayerSnapshotTest : public LayerHierarchyTestBase {
+class LayerSnapshotTest : public LayerSnapshotTestBase {
 protected:
-    LayerSnapshotTest() : LayerHierarchyTestBase() {
+    LayerSnapshotTest() : LayerSnapshotTestBase() {
         UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
     }
 
-    void createRootLayer(uint32_t id) override {
-        LayerHierarchyTestBase::createRootLayer(id);
-        setColor(id);
-    }
-
-    void createLayer(uint32_t id, uint32_t parentId) override {
-        LayerHierarchyTestBase::createLayer(id, parentId);
-        setColor(parentId);
-    }
-
-    void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
-        LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
-        setColor(id);
-    }
-
     void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
         if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
             mHierarchyBuilder.update(mLifecycleManager.getLayers(),
@@ -111,11 +96,7 @@
     LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
         return mSnapshotBuilder.getSnapshot(path);
     }
-
-    LayerHierarchyBuilder mHierarchyBuilder{{}};
     LayerSnapshotBuilder mSnapshotBuilder;
-    DisplayInfos mFrontEndDisplayInfos;
-    renderengine::ShadowSettings globalShadowSettings;
     static const std::vector<uint32_t> STARTING_ZORDER;
 };
 const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1,   11,   111, 12, 121,
@@ -872,4 +853,12 @@
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
     EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
 }
+
+TEST_F(LayerSnapshotTest, setShadowRadius) {
+    static constexpr float SHADOW_RADIUS = 123.f;
+    setShadowRadius(1, SHADOW_RADIUS);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(1)->shadowSettings.length, SHADOW_RADIUS);
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 173f941..87fae2c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -192,7 +192,7 @@
 
     // recordLayerHistory should be a noop
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
                                    LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
@@ -218,7 +218,7 @@
                                                                       kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
                                    LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
 }
@@ -273,7 +273,7 @@
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
 
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
                                    LayerHistory::LayerUpdateType::Buffer);
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -444,6 +444,63 @@
     }
 }
 
+TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+
+    using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
+
+    struct Compositor final : ICompositor {
+        VsyncIds vsyncIds;
+        bool committed = true;
+
+        void configure() override {}
+
+        bool commit(PhysicalDisplayId, const scheduler::FrameTargets& targets) override {
+            vsyncIds.clear();
+
+            for (const auto& [id, target] : targets) {
+                vsyncIds.emplace_back(id, target->vsyncId());
+            }
+
+            return committed;
+        }
+
+        CompositeResultsPerDisplay composite(PhysicalDisplayId,
+                                             const scheduler::FrameTargeters&) override {
+            CompositeResultsPerDisplay results;
+
+            for (const auto& [id, _] : vsyncIds) {
+                results.try_emplace(id,
+                                    CompositeResult{.compositionCoverage =
+                                                            CompositionCoverage::Hwc});
+            }
+
+            return results;
+        }
+
+        void sample() override {}
+    } compositor;
+
+    mScheduler->doFrameSignal(compositor, VsyncId(42));
+
+    const auto makeVsyncIds = [](VsyncId vsyncId) -> VsyncIds {
+        return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}};
+    };
+
+    EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds);
+
+    compositor.committed = false;
+    mScheduler->doFrameSignal(compositor, VsyncId(43));
+
+    // FrameTargets should be updated despite the skipped commit.
+    EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds);
+}
+
 class AttachedChoreographerTest : public SchedulerTest {
 protected:
     void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
index a2c54ac..db6df22 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -26,8 +26,6 @@
 
 namespace android {
 
-using aidl::android::hardware::graphics::common::HdrConversionCapability;
-using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
 using gui::aidl_utils::statusTFromBinderStatus;
 
@@ -66,17 +64,15 @@
             sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
     ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
 
-    std::vector<HdrConversionStrategy> strategies =
-            {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
-                                           GuiHdrConversionStrategyTag::passthrough)>),
-             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
-                                           GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
-             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
-                                           GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+    std::vector<gui::HdrConversionStrategy> strategies = {
+            gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::passthrough>(),
+            gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>(),
+            gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::forceHdrConversion>(),
+    };
     int32_t outPreferredHdrOutputType = 0;
 
-    for (HdrConversionStrategy strategy : strategies) {
-        binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+    for (const gui::HdrConversionStrategy& strategy : strategies) {
+        binder::Status status = sf->setHdrConversionStrategy(strategy, &outPreferredHdrOutputType);
 
         if (hdrOutputConversionSupport) {
             ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 151b178..014d07c 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -59,6 +59,12 @@
     MOCK_METHOD(void, scheduleFrame, (), (override));
     MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
 
+    void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
+        ftl::FakeGuard guard1(kMainThreadContext);
+        ftl::FakeGuard guard2(mDisplayLock);
+        Scheduler::onFrameSignal(compositor, vsyncId, TimePoint());
+    }
+
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
         return Scheduler::createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index dd998ba..b54392e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -604,6 +604,13 @@
         return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
     }
 
+    void injectLegacyLayer(sp<Layer> layer) {
+        mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer;
+    };
+
+    void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+
+    auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
@@ -644,8 +651,8 @@
     }
 
     auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
-
     auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+    auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; };
 
     auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 7af1da6..a1eda94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -30,13 +30,17 @@
 
 #include <scheduler/TimeKeeper.h>
 
+#include "FlagUtils.h"
 #include "Scheduler/VSyncDispatchTimerQueue.h"
 #include "Scheduler/VSyncTracker.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 using namespace testing;
 using namespace std::literals;
 
 namespace android::scheduler {
+using namespace com::android::graphics::surfaceflinger;
 
 class MockVSyncTracker : public VSyncTracker {
 public:
@@ -1061,6 +1065,52 @@
     EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
 }
 
+// TODO(b/304338314): Set the flag value instead of skipping the test
+TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) {
+    // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false);
+    if (flags::dont_skip_on_early()) GTEST_SKIP();
+
+    EXPECT_CALL(mMockClock, alarmAt(_, 500));
+    CountingCallback cb(mDispatch);
+    auto result =
+            mDispatch->schedule(cb,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
+    mMockClock.advanceBy(300);
+
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1200, *result);
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// TODO(b/304338314): Set the flag value instead of skipping the test
+TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) {
+    // SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true);
+    if (!flags::dont_skip_on_early()) GTEST_SKIP();
+
+    EXPECT_CALL(mMockClock, alarmAt(_, 500));
+    CountingCallback cb(mDispatch);
+    auto result =
+            mDispatch->schedule(cb,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
+    mMockClock.advanceBy(300);
+
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(200, *result);
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
 class VSyncDispatchTimerQueueEntryTest : public testing::Test {
 protected:
     nsecs_t const mPeriod = 1000;
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index e78f470..7c8e695 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -60,7 +60,12 @@
  *
  * This version of the extension cleans up a bug introduced in version 9
  */
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
+ *
+ * This version of the extension deprecates the last of grallocusage
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
 
 #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -151,6 +156,8 @@
  * pNext: NULL or a pointer to a structure extending this structure
  * format: value specifying the format the image will be created with
  * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ *
+ * DEPRECATED in SPEC_VERSION 10
  */
 typedef struct {
     VkStructureType                   sType;
@@ -167,6 +174,8 @@
  * format: value specifying the format the image will be created with
  * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
  * swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ *
+ * DEPRECATED in SPEC_VERSION 11
  */
 typedef struct {
     VkStructureType                   sType;
@@ -198,7 +207,7 @@
     const VkGrallocUsageInfoANDROID*  grallocUsageInfo,
     uint64_t*                         grallocUsage);
 
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
 typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
     VkDevice                          device,
     const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
@@ -245,7 +254,7 @@
     uint64_t*                         grallocUsage
 );
 
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
 VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
     VkDevice                          device,
     const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index bdba27e..5d7a4aa 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1456,6 +1456,7 @@
     }
 
     data->driver_device = dev;
+    data->driver_physical_device = physicalDevice;
 
     *pDevice = dev;
 
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 4d2bbd6..4b855e5 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
 
     VkDevice driver_device;
     DeviceDriverTable driver;
+    VkPhysicalDevice driver_physical_device;
 };
 
 bool OpenHAL();
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index dcef54d..0c48611 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -16,8 +16,10 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
 #include <android/hardware/graphics/common/1.0/types.h>
+#include <android/hardware_buffer.h>
 #include <grallocusage/GrallocUsageConversion.h>
 #include <graphicsenv/GraphicsEnv.h>
 #include <hardware/gralloc.h>
@@ -37,6 +39,7 @@
 #include "driver.h"
 
 using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+using DataSpace = aidl::android::hardware::graphics::common::Dataspace;
 using android::hardware::graphics::common::V1_0::BufferUsage;
 
 namespace vulkan {
@@ -532,61 +535,51 @@
     return native_format;
 }
 
-android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace,
-                                     PixelFormat pixelFormat) {
+DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace,
+                             PixelFormat pixelFormat) {
     switch (colorspace) {
         case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
-            return HAL_DATASPACE_V0_SRGB;
+            return DataSpace::SRGB;
         case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
-            return HAL_DATASPACE_DISPLAY_P3;
+            return DataSpace::DISPLAY_P3;
         case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
-            return HAL_DATASPACE_V0_SCRGB_LINEAR;
+            return DataSpace::SCRGB_LINEAR;
         case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
-            return HAL_DATASPACE_V0_SCRGB;
+            return DataSpace::SCRGB;
         case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
-            return HAL_DATASPACE_DCI_P3_LINEAR;
+            return DataSpace::DCI_P3_LINEAR;
         case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
-            return HAL_DATASPACE_DCI_P3;
+            return DataSpace::DCI_P3;
         case VK_COLOR_SPACE_BT709_LINEAR_EXT:
-            return HAL_DATASPACE_V0_SRGB_LINEAR;
+            return DataSpace::SRGB_LINEAR;
         case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
-            return HAL_DATASPACE_V0_SRGB;
+            return DataSpace::SRGB;
         case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
             if (pixelFormat == PixelFormat::RGBA_FP16) {
-                return static_cast<android_dataspace>(
-                    HAL_DATASPACE_STANDARD_BT2020 |
-                    HAL_DATASPACE_TRANSFER_LINEAR |
-                    HAL_DATASPACE_RANGE_EXTENDED);
+                return DataSpace::BT2020_LINEAR_EXTENDED;
             } else {
-                return HAL_DATASPACE_BT2020_LINEAR;
+                return DataSpace::BT2020_LINEAR;
             }
         case VK_COLOR_SPACE_HDR10_ST2084_EXT:
-            return static_cast<android_dataspace>(
-                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
-                HAL_DATASPACE_RANGE_FULL);
+            return DataSpace::BT2020_PQ;
         case VK_COLOR_SPACE_DOLBYVISION_EXT:
-            return static_cast<android_dataspace>(
-                HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_ST2084 |
-                HAL_DATASPACE_RANGE_FULL);
+            return DataSpace::BT2020_PQ;
         case VK_COLOR_SPACE_HDR10_HLG_EXT:
-            return static_cast<android_dataspace>(HAL_DATASPACE_BT2020_HLG);
+            return DataSpace::BT2020_HLG;
         case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
-            return static_cast<android_dataspace>(
-                HAL_DATASPACE_STANDARD_ADOBE_RGB |
-                HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL);
+            return DataSpace::ADOBE_RGB_LINEAR;
         case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
-            return HAL_DATASPACE_ADOBE_RGB;
-
+            return DataSpace::ADOBE_RGB;
         // Pass through is intended to allow app to provide data that is passed
         // to the display system without modification.
         case VK_COLOR_SPACE_PASS_THROUGH_EXT:
-            return HAL_DATASPACE_ARBITRARY;
+            return DataSpace::ARBITRARY;
 
         default:
             // This indicates that we don't know about the
             // dataspace specified and we should indicate that
             // it's unsupported
-            return HAL_DATASPACE_UNKNOWN;
+            return DataSpace::UNKNOWN;
     }
 }
 
@@ -1367,6 +1360,187 @@
     allocator->pfnFree(allocator->pUserData, swapchain);
 }
 
+static VkResult getProducerUsage(const VkDevice& device,
+                                 const VkSwapchainCreateInfoKHR* create_info,
+                                 const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
+                                 bool create_protected_swapchain,
+                                 uint64_t* producer_usage) {
+    // Get the physical device to query the appropriate producer usage
+    const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
+    const InstanceData& instance_data = GetData(pdev);
+    const InstanceDriverTable& instance_dispatch = instance_data.driver;
+    if (!instance_dispatch.GetPhysicalDeviceImageFormatProperties2 &&
+            !instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
+        uint64_t native_usage = 0;
+        void* usage_info_pNext = nullptr;
+        VkResult result;
+        VkImageCompressionControlEXT image_compression = {};
+        const auto& dispatch = GetData(device).driver;
+        if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+            ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+            VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+            gralloc_usage_info.sType =
+                VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+            gralloc_usage_info.format = create_info->imageFormat;
+            gralloc_usage_info.imageUsage = create_info->imageUsage;
+            gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+            // Look through the pNext chain for an image compression control struct
+            // if one is found AND the appropriate extensions are enabled,
+            // append it to be the gralloc usage pNext chain
+            const VkSwapchainCreateInfoKHR* create_infos = create_info;
+            while (create_infos->pNext) {
+                create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+                    create_infos->pNext);
+                switch (create_infos->sType) {
+                    case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                        const VkImageCompressionControlEXT* compression_infos =
+                            reinterpret_cast<const VkImageCompressionControlEXT*>(
+                                create_infos);
+                        image_compression = *compression_infos;
+                        image_compression.pNext = nullptr;
+                        usage_info_pNext = &image_compression;
+                    } break;
+
+                    default:
+                        // Ignore all other info structs
+                        break;
+                }
+            }
+            gralloc_usage_info.pNext = usage_info_pNext;
+
+            result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+                device, &gralloc_usage_info, &native_usage);
+            ATRACE_END();
+            if (result != VK_SUCCESS) {
+                ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+                return VK_ERROR_SURFACE_LOST_KHR;
+            }
+        } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+            ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+            VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+            gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+            gralloc_usage_info.format = create_info->imageFormat;
+            gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+            // Look through the pNext chain for an image compression control struct
+            // if one is found AND the appropriate extensions are enabled,
+            // append it to be the gralloc usage pNext chain
+            const VkSwapchainCreateInfoKHR* create_infos = create_info;
+            while (create_infos->pNext) {
+                create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+                    create_infos->pNext);
+                switch (create_infos->sType) {
+                    case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                        const VkImageCompressionControlEXT* compression_infos =
+                            reinterpret_cast<const VkImageCompressionControlEXT*>(
+                                create_infos);
+                        image_compression = *compression_infos;
+                        image_compression.pNext = nullptr;
+                        usage_info_pNext = &image_compression;
+                    } break;
+
+                    default:
+                        // Ignore all other info structs
+                        break;
+                }
+            }
+            gralloc_usage_info.pNext = usage_info_pNext;
+
+            result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+                device, &gralloc_usage_info, &native_usage);
+            ATRACE_END();
+            if (result != VK_SUCCESS) {
+                ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+                return VK_ERROR_SURFACE_LOST_KHR;
+            }
+        } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+            uint64_t consumer_usage, producer_usage;
+            ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
+            result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+                device, create_info->imageFormat, create_info->imageUsage,
+                swapchain_image_usage, &consumer_usage, &producer_usage);
+            ATRACE_END();
+            if (result != VK_SUCCESS) {
+                ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+                return VK_ERROR_SURFACE_LOST_KHR;
+            }
+            native_usage =
+                convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
+        } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+            ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
+            int32_t legacy_usage = 0;
+            result = dispatch.GetSwapchainGrallocUsageANDROID(
+                device, create_info->imageFormat, create_info->imageUsage,
+                &legacy_usage);
+            ATRACE_END();
+            if (result != VK_SUCCESS) {
+                ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
+                return VK_ERROR_SURFACE_LOST_KHR;
+            }
+            native_usage = static_cast<uint64_t>(legacy_usage);
+        }
+        *producer_usage = native_usage;
+
+        return VK_SUCCESS;
+    }
+
+    // call GetPhysicalDeviceImageFormatProperties2KHR
+    VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+        .pNext = nullptr,
+        .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+    };
+
+    // AHB does not have an sRGB format so we can't pass it to GPDIFP
+    // We need to convert the format to unorm if it is srgb
+    VkFormat format = create_info->imageFormat;
+    if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+        format = VK_FORMAT_R8G8B8A8_UNORM;
+    }
+
+    VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+        .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+        .pNext = &external_image_format_info,
+        .format = format,
+        .type = VK_IMAGE_TYPE_2D,
+        .tiling = VK_IMAGE_TILING_OPTIMAL,
+        .usage = create_info->imageUsage,
+        .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+    };
+
+    VkAndroidHardwareBufferUsageANDROID ahb_usage;
+    ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+    ahb_usage.pNext = nullptr;
+
+    VkImageFormatProperties2 image_format_properties;
+    image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+    image_format_properties.pNext = &ahb_usage;
+
+    if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
+        VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
+            pdev, &image_format_info, &image_format_properties);
+        if (result != VK_SUCCESS) {
+            ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    }
+    else {
+        VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
+            pdev, &image_format_info,
+            &image_format_properties);
+        if (result != VK_SUCCESS) {
+            ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
+                result);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+    }
+
+    *producer_usage = ahb_usage.androidHardwareBufferUsage;
+
+    return VK_SUCCESS;
+}
+
 VKAPI_ATTR
 VkResult CreateSwapchainKHR(VkDevice device,
                             const VkSwapchainCreateInfoKHR* create_info,
@@ -1393,9 +1567,9 @@
 
     PixelFormat native_pixel_format =
         GetNativePixelFormat(create_info->imageFormat);
-    android_dataspace native_dataspace =
+    DataSpace native_dataspace =
         GetNativeDataspace(create_info->imageColorSpace, native_pixel_format);
-    if (native_dataspace == HAL_DATASPACE_UNKNOWN) {
+    if (native_dataspace == DataSpace::UNKNOWN) {
         ALOGE(
             "CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
             "failed: Unsupported color space",
@@ -1501,8 +1675,9 @@
     }
 
     /* Respect consumer default dataspace upon HAL_DATASPACE_ARBITRARY. */
-    if (native_dataspace != HAL_DATASPACE_ARBITRARY) {
-        err = native_window_set_buffers_data_space(window, native_dataspace);
+    if (native_dataspace != DataSpace::ARBITRARY) {
+        err = native_window_set_buffers_data_space(
+            window, static_cast<android_dataspace_t>(native_dataspace));
         if (err != android::OK) {
             ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
                   native_dataspace, strerror(-err), err);
@@ -1601,120 +1776,48 @@
         num_images = 1;
     }
 
+    // Look through the create_info pNext chain passed to createSwapchainKHR
+    // for an image compression control struct.
+    // if one is found AND the appropriate extensions are enabled, create a
+    // VkImageCompressionControlEXT structure to pass on to VkImageCreateInfo
+    // TODO check for imageCompressionControlSwapchain feature is enabled
     void* usage_info_pNext = nullptr;
     VkImageCompressionControlEXT image_compression = {};
-    uint64_t native_usage = 0;
-    if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
-        ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
-        VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
-        gralloc_usage_info.sType =
-            VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
-        gralloc_usage_info.format = create_info->imageFormat;
-        gralloc_usage_info.imageUsage = create_info->imageUsage;
-        gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+    const VkSwapchainCreateInfoKHR* create_infos = create_info;
+    while (create_infos->pNext) {
+        create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+        switch (create_infos->sType) {
+            case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+                const VkImageCompressionControlEXT* compression_infos =
+                    reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+                image_compression = *compression_infos;
+                image_compression.pNext = nullptr;
+                usage_info_pNext = &image_compression;
+            } break;
 
-        // Look through the pNext chain for an image compression control struct
-        // if one is found AND the appropriate extensions are enabled,
-        // append it to be the gralloc usage pNext chain
-        const VkSwapchainCreateInfoKHR* create_infos = create_info;
-        while (create_infos->pNext) {
-            create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
-                create_infos->pNext);
-            switch (create_infos->sType) {
-                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
-                    const VkImageCompressionControlEXT* compression_infos =
-                        reinterpret_cast<const VkImageCompressionControlEXT*>(
-                            create_infos);
-                    image_compression = *compression_infos;
-                    image_compression.pNext = nullptr;
-                    usage_info_pNext = &image_compression;
-                } break;
-
-                default:
-                    // Ignore all other info structs
-                    break;
-            }
+            default:
+                // Ignore all other info structs
+                break;
         }
-        gralloc_usage_info.pNext = usage_info_pNext;
-
-        result = dispatch.GetSwapchainGrallocUsage4ANDROID(
-            device, &gralloc_usage_info, &native_usage);
-        ATRACE_END();
-        if (result != VK_SUCCESS) {
-            ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-    } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
-        ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
-        VkGrallocUsageInfoANDROID gralloc_usage_info = {};
-        gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
-        gralloc_usage_info.format = create_info->imageFormat;
-        gralloc_usage_info.imageUsage = create_info->imageUsage;
-
-        // Look through the pNext chain for an image compression control struct
-        // if one is found AND the appropriate extensions are enabled,
-        // append it to be the gralloc usage pNext chain
-        const VkSwapchainCreateInfoKHR* create_infos = create_info;
-        while (create_infos->pNext) {
-            create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
-                create_infos->pNext);
-            switch (create_infos->sType) {
-                case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
-                    const VkImageCompressionControlEXT* compression_infos =
-                        reinterpret_cast<const VkImageCompressionControlEXT*>(
-                            create_infos);
-                    image_compression = *compression_infos;
-                    image_compression.pNext = nullptr;
-                    usage_info_pNext = &image_compression;
-                } break;
-
-                default:
-                    // Ignore all other info structs
-                    break;
-            }
-        }
-        gralloc_usage_info.pNext = usage_info_pNext;
-
-        result = dispatch.GetSwapchainGrallocUsage3ANDROID(
-            device, &gralloc_usage_info, &native_usage);
-        ATRACE_END();
-        if (result != VK_SUCCESS) {
-            ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-    } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
-        uint64_t consumer_usage, producer_usage;
-        ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
-        result = dispatch.GetSwapchainGrallocUsage2ANDROID(
-            device, create_info->imageFormat, create_info->imageUsage,
-            swapchain_image_usage, &consumer_usage, &producer_usage);
-        ATRACE_END();
-        if (result != VK_SUCCESS) {
-            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-        native_usage =
-            convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
-    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
-        ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
-        int32_t legacy_usage = 0;
-        result = dispatch.GetSwapchainGrallocUsageANDROID(
-            device, create_info->imageFormat, create_info->imageUsage,
-            &legacy_usage);
-        ATRACE_END();
-        if (result != VK_SUCCESS) {
-            ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
-            return VK_ERROR_SURFACE_LOST_KHR;
-        }
-        native_usage = static_cast<uint64_t>(legacy_usage);
     }
-    native_usage |= surface.consumer_usage;
 
-    bool createProtectedSwapchain = false;
+    // Get the appropriate native_usage for the images
+    // Get the consumer usage
+    uint64_t native_usage = surface.consumer_usage;
+    // Determine if the swapchain is protected
+    bool create_protected_swapchain = false;
     if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {
-        createProtectedSwapchain = true;
+        create_protected_swapchain = true;
         native_usage |= BufferUsage::PROTECTED;
     }
+    // Get the producer usage
+    uint64_t producer_usage;
+    result = getProducerUsage(device, create_info, swapchain_image_usage, create_protected_swapchain, &producer_usage);
+    if (result != VK_SUCCESS) {
+        return result;
+    }
+    native_usage |= producer_usage;
+
     err = native_window_set_usage(window, native_usage);
     if (err != android::OK) {
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
@@ -1742,8 +1845,10 @@
     void* mem = allocator->pfnAllocation(allocator->pUserData,
                                          sizeof(Swapchain), alignof(Swapchain),
                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
     if (!mem)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
+
     Swapchain* swapchain = new (mem)
         Swapchain(surface, num_images, create_info->presentMode,
                   TranslateVulkanToNativeTransform(create_info->preTransform),
@@ -1767,7 +1872,7 @@
     VkImageCreateInfo image_create = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
         .pNext = nullptr,
-        .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+        .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
         .imageType = VK_IMAGE_TYPE_2D,
         .format = create_info->imageFormat,
         .extent = {