Merge "Add Unit Tests to Check Default Strategy Setup"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 0b69829..fe91341 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -183,6 +183,7 @@
 #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
 #define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
 #define CGROUPFS_DIR "/sys/fs/cgroup"
+#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -766,7 +767,7 @@
 }
 
 void Dumpstate::PrintHeader() const {
-    std::string build, fingerprint, radio, bootloader, network;
+    std::string build, fingerprint, radio, bootloader, network, sdkversion;
     char date[80];
 
     build = android::base::GetProperty("ro.build.display.id", "(unknown)");
@@ -774,6 +775,7 @@
     radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
     bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
     network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
+    sdkversion = android::base::GetProperty("ro.build.version.sdk", "(unknown)");
     strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
 
     printf("========================================================\n");
@@ -791,9 +793,10 @@
     if (module_metadata_version != 0) {
         printf("Module Metadata version: %" PRId64 "\n", module_metadata_version);
     }
-    printf("SDK extension versions [r=%s s=%s]\n",
-           android::base::GetProperty("build.version.extensions.r", "-").c_str(),
-           android::base::GetProperty("build.version.extensions.s", "-").c_str());
+    printf("Android SDK version: %s\n", sdkversion.c_str());
+    printf("SDK extensions: ");
+    RunCommandToFd(STDOUT_FILENO, "", {SDK_EXT_INFO, "--header"},
+                   CommandOptions::WithTimeout(1).Always().DropRoot().Build());
 
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
@@ -1026,7 +1029,7 @@
         MYLOGE("Could not open %s to dump incident report.\n", path.c_str());
         return;
     }
-    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build());
+    RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(20).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         // Use a different name from "incident.proto"
@@ -1084,7 +1087,7 @@
         return;
     }
     RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
-                   CommandOptions::WithTimeout(120).Build());
+                   CommandOptions::WithTimeout(10).Build());
     bool empty = 0 == lseek(fd, 0, SEEK_END);
     if (!empty) {
         ds.AddZipEntry("visible_windows.zip", path);
@@ -1423,7 +1426,9 @@
 // Dump all of the files that make up the vendor interface.
 // See the files listed in dumpFileList() for the latest list of files.
 static void DumpVintf() {
-    const auto vintfFiles = android::vintf::details::dumpFileList();
+
+    const std::string sku = android::base::GetProperty("ro.boot.product.hardware.sku", "");
+    const auto vintfFiles = android::vintf::details::dumpFileList(sku);
     for (const auto vintfFile : vintfFiles) {
         struct stat st;
         if (stat(vintfFile.c_str(), &st) == 0) {
@@ -1890,6 +1895,9 @@
     DumpFile("PSI memory", "/proc/pressure/memory");
     DumpFile("PSI io", "/proc/pressure/io");
 
+    RunCommand("SDK EXTENSIONS", {SDK_EXT_INFO, "--dump"},
+               CommandOptions::WithTimeout(10).Always().DropRoot().Build());
+
     if (dump_pool_) {
         RETURN_IF_USER_DENIED_CONSENT();
         WaitForTask(std::move(dump_traces));
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 0727383..a99ccae 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -326,16 +326,41 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "linkToDeath failure");
     }
 
+    auto it = mNameToService.find(name);
+    if (it != mNameToService.end()) {
+        const Service& existing = it->second;
+
+        // We could do better than this because if the other service dies, it
+        // may not have an entry here. However, this case is unlikely. We are
+        // only trying to detect when two different services are accidentally installed.
+
+        if (existing.ctx.uid != ctx.uid) {
+            LOG(WARNING) << "Service '" << name << "' originally registered from UID "
+                         << existing.ctx.uid << " but it is now being registered from UID "
+                         << ctx.uid << ". Multiple instances installed?";
+        }
+
+        if (existing.ctx.sid != ctx.sid) {
+            LOG(WARNING) << "Service '" << name << "' originally registered from SID "
+                         << existing.ctx.sid << " but it is now being registered from SID "
+                         << ctx.sid << ". Multiple instances installed?";
+        }
+
+        LOG(INFO) << "Service '" << name << "' originally registered from PID "
+                  << existing.ctx.debugPid << " but it is being registered again from PID "
+                  << ctx.debugPid
+                  << ". Bad state? Late death notification? Multiple instances installed?";
+    }
+
     // Overwrite the old service if it exists
-    mNameToService[name] = Service {
-        .binder = binder,
-        .allowIsolated = allowIsolated,
-        .dumpPriority = dumpPriority,
-        .debugPid = ctx.debugPid,
+    mNameToService[name] = Service{
+            .binder = binder,
+            .allowIsolated = allowIsolated,
+            .dumpPriority = dumpPriority,
+            .ctx = ctx,
     };
 
-    auto it = mNameToRegistrationCallback.find(name);
-    if (it != mNameToRegistrationCallback.end()) {
+    if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) {
         for (const sp<IServiceCallback>& cb : it->second) {
             mNameToService[name].guaranteeClient = true;
             // permission checked in registerForNotifications
@@ -571,7 +596,7 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
@@ -707,7 +732,7 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+    if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
         LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
         return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
     }
@@ -754,7 +779,7 @@
     for (auto const& [name, service] : mNameToService) {
         ServiceDebugInfo info;
         info.name = name;
-        info.debugPid = service.debugPid;
+        info.debugPid = service.ctx.debugPid;
 
         outReturn->push_back(std::move(info));
     }
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index c6db697..07b79f8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -74,7 +74,7 @@
         int32_t dumpPriority;
         bool hasClients = false; // notifications sent on true -> false.
         bool guaranteeClient = false; // forces the client check to true
-        pid_t debugPid = 0; // the process in which this service runs
+        Access::CallingContext ctx;   // process that originally registers this
 
         // the number of clients of the service, including servicemanager itself
         ssize_t getNodeStrongRefCount();
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 415080d..0026e82 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/sensor.h>
+#include <ftl/flags.h>
 #include <input/Input.h>
 #include <input/KeyCharacterMap.h>
 #include <unordered_map>
@@ -105,12 +106,18 @@
 };
 
 enum class InputDeviceLightType : int32_t {
-    MONO = 0,
+    INPUT = 0,
     PLAYER_ID = 1,
-    RGB = 2,
-    MULTI_COLOR = 3,
+    KEYBOARD_BACKLIGHT = 2,
 
-    ftl_last = MULTI_COLOR
+    ftl_last = KEYBOARD_BACKLIGHT
+};
+
+enum class InputDeviceLightCapability : uint32_t {
+    /** Capability to change brightness of the light */
+    BRIGHTNESS = 0x00000001,
+    /** Capability to change color of the light */
+    RGB = 0x00000002,
 };
 
 struct InputDeviceSensorInfo {
@@ -171,14 +178,17 @@
 
 struct InputDeviceLightInfo {
     explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type,
+                                  ftl::Flags<InputDeviceLightCapability> capabilityFlags,
                                   int32_t ordinal)
-          : name(name), id(id), type(type), ordinal(ordinal) {}
+          : name(name), id(id), type(type), capabilityFlags(capabilityFlags), ordinal(ordinal) {}
     // Name string of the light.
     std::string name;
     // Light id
     int32_t id;
     // Type of the light.
     InputDeviceLightType type;
+    // Light capabilities.
+    ftl::Flags<InputDeviceLightCapability> capabilityFlags;
     // Ordinal of the light
     int32_t ordinal;
 };
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index bfcf39a..11c8e5d 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -39,7 +39,6 @@
 #include <sys/resource.h>
 #include <unistd.h>
 
-#include "Static.h"
 #include "binder_module.h"
 
 #if LOG_NDEBUG
@@ -124,46 +123,43 @@
         return "unknown";
 }
 
-static const void* printBinderTransactionData(TextOutput& out, const void* data)
-{
+static const void* printBinderTransactionData(std::ostream& out, const void* data) {
     const binder_transaction_data* btd =
         (const binder_transaction_data*)data;
     if (btd->target.handle < 1024) {
         /* want to print descriptors in decimal; guess based on value */
-        out << "target.desc=" << btd->target.handle;
+        out << "\ttarget.desc=" << btd->target.handle;
     } else {
-        out << "target.ptr=" << btd->target.ptr;
+        out << "\ttarget.ptr=" << btd->target.ptr;
     }
-    out << " (cookie " << btd->cookie << ")" << endl
-        << "code=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << endl
-        << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
-        << " bytes)" << endl
-        << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
-        << " bytes)";
+    out << "\t (cookie " << btd->cookie << ")"
+        << "\n"
+        << "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
+        << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
+        << "\n"
+        << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
     return btd+1;
 }
 
-static const void* printReturnCommand(TextOutput& out, const void* _cmd)
-{
+static const void* printReturnCommand(std::ostream& out, const void* _cmd) {
     static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     uint32_t code = (uint32_t)*cmd++;
     size_t cmdIndex = code & 0xff;
     if (code == BR_ERROR) {
-        out << "BR_ERROR: " << (void*)(uint64_t)(*cmd++) << endl;
+        out << "\tBR_ERROR: " << (void*)(uint64_t)(*cmd++) << "\n";
         return cmd;
     } else if (cmdIndex >= N) {
-        out << "Unknown reply: " << code << endl;
+        out << "\tUnknown reply: " << code << "\n";
         return cmd;
     }
-    out << kReturnStrings[cmdIndex];
+    out << "\t" << kReturnStrings[cmdIndex];
 
     switch (code) {
         case BR_TRANSACTION:
         case BR_REPLY: {
-            out << ": " << indent;
-            cmd = (const int32_t *)printBinderTransactionData(out, cmd);
-            out << dedent;
+            out << ": ";
+            cmd = (const int32_t*)printBinderTransactionData(out, cmd);
         } break;
 
         case BR_ACQUIRE_RESULT: {
@@ -200,19 +196,18 @@
             break;
     }
 
-    out << endl;
+    out << "\n";
     return cmd;
 }
 
-static const void* printCommand(TextOutput& out, const void* _cmd)
-{
+static const void* printCommand(std::ostream& out, const void* _cmd) {
     static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
     const int32_t* cmd = (const int32_t*)_cmd;
     uint32_t code = (uint32_t)*cmd++;
     size_t cmdIndex = code & 0xff;
 
     if (cmdIndex >= N) {
-        out << "Unknown command: " << code << endl;
+        out << "Unknown command: " << code << "\n";
         return cmd;
     }
     out << kCommandStrings[cmdIndex];
@@ -220,9 +215,8 @@
     switch (code) {
         case BC_TRANSACTION:
         case BC_REPLY: {
-            out << ": " << indent;
-            cmd = (const int32_t *)printBinderTransactionData(out, cmd);
-            out << dedent;
+            out << ": ";
+            cmd = (const int32_t*)printBinderTransactionData(out, cmd);
         } break;
 
         case BC_ACQUIRE_RESULT: {
@@ -274,7 +268,7 @@
             break;
     }
 
-    out << endl;
+    out << "\n";
     return cmd;
 }
 
@@ -548,8 +542,10 @@
         if (IN < sizeof(int32_t)) return result;
         cmd = mIn.readInt32();
         IF_LOG_COMMANDS() {
-            alog << "Processing top-level Command: "
-                 << getReturnString(cmd) << endl;
+            std::ostringstream logStream;
+            logStream << "Processing top-level Command: " << getReturnString(cmd) << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 
         pthread_mutex_lock(&mProcess->mThreadCountLock);
@@ -726,10 +722,11 @@
     flags |= TF_ACCEPT_FDS;
 
     IF_LOG_TRANSACTIONS() {
-        TextOutput::Bundle _b(alog);
-        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
-            << handle << " / code " << TypeCode(code) << ": "
-            << indent << data << dedent << endl;
+        std::ostringstream logStream;
+        logStream << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " << handle
+                  << " / code " << TypeCode(code) << ": \t" << data << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
@@ -774,11 +771,15 @@
         #endif
 
         IF_LOG_TRANSACTIONS() {
-            TextOutput::Bundle _b(alog);
-            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
-                << handle << ": ";
-            if (reply) alog << indent << *reply << dedent << endl;
-            else alog << "(none requested)" << endl;
+            std::ostringstream logStream;
+            logStream << "BR_REPLY thr " << (void*)pthread_self() << " / hand " << handle << ": ";
+            if (reply)
+                logStream << "\t" << *reply << "\n";
+            else
+                logStream << "(none requested)"
+                          << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
     } else {
         err = waitForResponse(nullptr, nullptr);
@@ -920,8 +921,10 @@
         cmd = (uint32_t)mIn.readInt32();
 
         IF_LOG_COMMANDS() {
-            alog << "Processing waitForResponse Command: "
-                << getReturnString(cmd) << endl;
+            std::ostringstream logStream;
+            logStream << "Processing waitForResponse Command: " << getReturnString(cmd) << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 
         switch (cmd) {
@@ -1033,17 +1036,19 @@
     }
 
     IF_LOG_COMMANDS() {
-        TextOutput::Bundle _b(alog);
+        std::ostringstream logStream;
         if (outAvail != 0) {
-            alog << "Sending commands to driver: " << indent;
+            logStream << "Sending commands to driver: ";
             const void* cmds = (const void*)bwr.write_buffer;
-            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
-            alog << HexDump(cmds, bwr.write_size) << endl;
-            while (cmds < end) cmds = printCommand(alog, cmds);
-            alog << dedent;
+            const void* end = ((const uint8_t*)cmds) + bwr.write_size;
+            logStream << "\t" << HexDump(cmds, bwr.write_size) << "\n";
+            while (cmds < end) cmds = printCommand(logStream, cmds);
         }
-        alog << "Size of receive buffer: " << bwr.read_size
-            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
+        logStream << "Size of receive buffer: " << bwr.read_size << ", needRead: " << needRead
+                  << ", doReceive: " << doReceive << "\n";
+
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     // Return immediately if there is nothing to do.
@@ -1054,7 +1059,10 @@
     status_t err;
     do {
         IF_LOG_COMMANDS() {
-            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
+            std::ostringstream logStream;
+            logStream << "About to read/write, write size = " << mOut.dataSize() << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
 #if defined(__ANDROID__)
         if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
@@ -1068,14 +1076,20 @@
             err = -EBADF;
         }
         IF_LOG_COMMANDS() {
-            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
+            std::ostringstream logStream;
+            logStream << "Finished read/write, write size = " << mOut.dataSize() << "\n";
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
     } while (err == -EINTR);
 
     IF_LOG_COMMANDS() {
-        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
-            << bwr.write_consumed << " (of " << mOut.dataSize()
-                        << "), read consumed: " << bwr.read_consumed << endl;
+        std::ostringstream logStream;
+        logStream << "Our err: " << (void*)(intptr_t)err
+                  << ", write consumed: " << bwr.write_consumed << " (of " << mOut.dataSize()
+                  << "), read consumed: " << bwr.read_consumed << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
 
     if (err >= NO_ERROR) {
@@ -1096,14 +1110,15 @@
             mIn.setDataPosition(0);
         }
         IF_LOG_COMMANDS() {
-            TextOutput::Bundle _b(alog);
-            alog << "Remaining data size: " << mOut.dataSize() << endl;
-            alog << "Received commands from driver: " << indent;
+            std::ostringstream logStream;
+            logStream << "Remaining data size: " << mOut.dataSize() << "\n";
+            logStream << "Received commands from driver: ";
             const void* cmds = mIn.data();
             const void* end = mIn.data() + mIn.dataSize();
-            alog << HexDump(cmds, mIn.dataSize()) << endl;
-            while (cmds < end) cmds = printReturnCommand(alog, cmds);
-            alog << dedent;
+            logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n";
+            while (cmds < end) cmds = printReturnCommand(logStream, cmds);
+            std::string message = logStream.str();
+            ALOGI("%s", message.c_str());
         }
         return NO_ERROR;
     }
@@ -1285,15 +1300,15 @@
             Parcel reply;
             status_t error;
             IF_LOG_TRANSACTIONS() {
-                TextOutput::Bundle _b(alog);
-                alog << "BR_TRANSACTION thr " << (void*)pthread_self()
-                    << " / obj " << tr.target.ptr << " / code "
-                    << TypeCode(tr.code) << ": " << indent << buffer
-                    << dedent << endl
-                    << "Data addr = "
-                    << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
-                    << ", offsets addr="
-                    << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
+                std::ostringstream logStream;
+                logStream << "BR_TRANSACTION thr " << (void*)pthread_self() << " / obj "
+                          << tr.target.ptr << " / code " << TypeCode(tr.code) << ": \t" << buffer
+                          << "\n"
+                          << "Data addr = " << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
+                          << ", offsets addr="
+                          << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << "\n";
+                std::string message = logStream.str();
+                ALOGI("%s", message.c_str());
             }
             if (tr.target.ptr) {
                 // We only have a weak reference on the target object, so we must first try to
@@ -1329,21 +1344,21 @@
                 sendReply(reply, (tr.flags & kForwardReplyFlags));
             } else {
                 if (error != OK) {
-                    alog << "oneway function results for code " << tr.code
-                         << " on binder at "
-                         << reinterpret_cast<void*>(tr.target.ptr)
-                         << " will be dropped but finished with status "
-                         << statusToString(error);
+                    std::ostringstream logStream;
+                    logStream << "oneway function results for code " << tr.code << " on binder at "
+                              << reinterpret_cast<void*>(tr.target.ptr)
+                              << " will be dropped but finished with status "
+                              << statusToString(error);
 
                     // ideally we could log this even when error == OK, but it
                     // causes too much logspam because some manually-written
                     // interfaces have clients that call methods which always
                     // write results, sometimes as oneway methods.
                     if (reply.dataSize() != 0) {
-                         alog << " and reply parcel size " << reply.dataSize();
+                        logStream << " and reply parcel size " << reply.dataSize();
                     }
-
-                    alog << endl;
+                    std::string message = logStream.str();
+                    ALOGI("%s", message.c_str());
                 }
                 LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
             }
@@ -1358,9 +1373,11 @@
             mPropagateWorkSource = origPropagateWorkSet;
 
             IF_LOG_TRANSACTIONS() {
-                TextOutput::Bundle _b(alog);
-                alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
-                    << tr.target.ptr << ": " << indent << reply << dedent << endl;
+                std::ostringstream logStream;
+                logStream << "BC_REPLY thr " << (void*)pthread_self() << " / obj " << tr.target.ptr
+                          << ": \t" << reply << "\n";
+                std::string message = logStream.str();
+                ALOGI("%s", message.c_str());
             }
 
         }
@@ -1481,7 +1498,10 @@
                                 const binder_size_t* /*objects*/, size_t /*objectsSize*/) {
     //ALOGI("Freeing parcel %p", &parcel);
     IF_LOG_COMMANDS() {
-        alog << "Writing BC_FREE_BUFFER for " << data << endl;
+        std::ostringstream logStream;
+        logStream << "Writing BC_FREE_BUFFER for " << data << "\n";
+        std::string message = logStream.str();
+        ALOGI("%s", message.c_str());
     }
     ALOG_ASSERT(data != NULL, "Called with NULL data");
     IPCThreadState* state = self();
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 8887572..8333298 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2639,8 +2639,7 @@
     return OK;
 }
 
-void Parcel::print(TextOutput& to, uint32_t /*flags*/) const
-{
+void Parcel::print(std::ostream& to, uint32_t /*flags*/) const {
     to << "Parcel(";
 
     if (errorCheck() != NO_ERROR) {
@@ -2648,7 +2647,7 @@
         to << "Error: " << (void*)(intptr_t)err << " \"" << strerror(-err) << "\"";
     } else if (dataSize() > 0) {
         const uint8_t* DATA = data();
-        to << indent << HexDump(DATA, dataSize()) << dedent;
+        to << "\t" << HexDump(DATA, dataSize());
 #ifdef BINDER_WITH_KERNEL_IPC
         if (const auto* kernelFields = maybeKernelFields()) {
             const binder_size_t* OBJS = kernelFields->mObjects;
@@ -2656,8 +2655,7 @@
             for (size_t i = 0; i < N; i++) {
                 const flat_binder_object* flat =
                         reinterpret_cast<const flat_binder_object*>(DATA + OBJS[i]);
-                to << endl
-                   << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
+                to << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
                    << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder;
             }
         }
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index c0e36c4..c411f4f 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -41,7 +41,7 @@
 #if RPC_FLAKE_PRONE
 void rpcMaybeWaitToFlake() {
     [[clang::no_destroy]] static std::random_device r;
-    [[clang::no_destroy]] static std::mutex m;
+    [[clang::no_destroy]] static RpcMutex m;
     unsigned num;
     {
         RpcMutexLockGuard lock(m);
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index a0ade50..5dd1f90 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -39,11 +39,10 @@
 
 static void textOutputPrinter(void* cookie, const char* txt)
 {
-    ((TextOutput*)cookie)->print(txt, strlen(txt));
+    ((std::ostream*)cookie)->write(txt, strlen(txt));
 }
 
-TextOutput& operator<<(TextOutput& to, const TypeCode& val)
-{
+std::ostream& operator<<(std::ostream& to, const TypeCode& val) {
     printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to);
     return to;
 }
@@ -61,8 +60,7 @@
     else mAlignment = 1;
 }
 
-TextOutput& operator<<(TextOutput& to, const HexDump& val)
-{
+std::ostream& operator<<(std::ostream& to, const HexDump& val) {
     printHexData(0, val.buffer(), val.size(), val.bytesPerLine(),
         val.singleLineCutoff(), val.alignment(), val.carrayStyle(),
         textOutputPrinter, (void*)&to);
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5469239..6de6ce8 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -595,7 +595,7 @@
     // uid.
     uid_t               readCallingWorkSourceUid() const;
 
-    void                print(TextOutput& to, uint32_t flags = 0) const;
+    void print(std::ostream& to, uint32_t flags = 0) const;
 
 private:
     // `objects` and `objectsSize` always 0 for RPC Parcels.
@@ -1594,8 +1594,7 @@
 
 // ---------------------------------------------------------------------------
 
-inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
-{
+inline std::ostream& operator<<(std::ostream& to, const Parcel& parcel) {
     parcel.print(to);
     return to;
 }
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index bf9c92b..eb98042 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -94,7 +94,7 @@
     uint32_t mCode;
 };
 
-TextOutput& operator<<(TextOutput& to, const TypeCode& val);
+std::ostream& operator<<(std::ostream& to, const TypeCode& val);
 
 class HexDump
 {
@@ -123,7 +123,7 @@
     bool mCArrayStyle;
 };
 
-TextOutput& operator<<(TextOutput& to, const HexDump& val);
+std::ostream& operator<<(std::ostream& to, const HexDump& val);
 inline TextOutput& operator<<(TextOutput& to,
                               decltype(std::endl<char,
                                        std::char_traits<char>>)
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 33d28e6..8ae7537 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -182,4 +182,8 @@
     name: "libbinder_ndk",
     symbol_file: "libbinder_ndk.map.txt",
     first_version: "29",
+    export_header_libs: [
+        "libbinder_ndk_headers",
+        "libbinder_ndk_helper_headers",
+    ],
 }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 21b0354..9a6d4df 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -85,6 +85,11 @@
     return base::StringPrintf("unexpected state %d", wstatus);
 }
 
+static void debugBacktrace(pid_t pid) {
+    std::cerr << "TAKING BACKTRACE FOR PID " << pid << std::endl;
+    system((std::string("debuggerd -b ") + std::to_string(pid)).c_str());
+}
+
 class Process {
 public:
     Process(Process&&) = default;
@@ -125,6 +130,8 @@
     // Kill the process. Avoid if possible. Shutdown gracefully via an RPC instead.
     void terminate() { kill(mPid, SIGTERM); }
 
+    pid_t getPid() { return mPid; }
+
 private:
     std::function<void(int wstatus)> mCustomExitStatusCheck;
     pid_t mPid = 0;
@@ -173,7 +180,9 @@
 
             wp<RpcSession> weakSession = session;
             session = nullptr;
-            EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session";
+
+            EXPECT_EQ(nullptr, weakSession.promote())
+                    << (debugBacktrace(host.getPid()), debugBacktrace(getpid()), "Leaked session");
         }
     }
 };
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index fc0b1ee..72bd1fb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -627,7 +627,6 @@
 
 SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
       : mId(other.mId),
-        mForceSynchronous(other.mForceSynchronous),
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeupStart(other.mEarlyWakeupStart),
@@ -662,7 +661,6 @@
 
 status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
     const uint64_t transactionId = parcel->readUint64();
-    const uint32_t forceSynchronous = parcel->readUint32();
     const uint32_t transactionNestCount = parcel->readUint32();
     const bool animation = parcel->readBool();
     const bool earlyWakeupStart = parcel->readBool();
@@ -739,7 +737,6 @@
 
     // Parsing was successful. Update the object.
     mId = transactionId;
-    mForceSynchronous = forceSynchronous;
     mTransactionNestCount = transactionNestCount;
     mAnimation = animation;
     mEarlyWakeupStart = earlyWakeupStart;
@@ -770,7 +767,6 @@
     const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
 
     parcel->writeUint64(mId);
-    parcel->writeUint32(mForceSynchronous);
     parcel->writeUint32(mTransactionNestCount);
     parcel->writeBool(mAnimation);
     parcel->writeBool(mEarlyWakeupStart);
@@ -890,7 +886,6 @@
     mListenerCallbacks.clear();
     mInputWindowCommands.clear();
     mMayContainBuffer = false;
-    mForceSynchronous = 0;
     mTransactionNestCount = 0;
     mAnimation = false;
     mEarlyWakeupStart = false;
@@ -1003,27 +998,25 @@
     Vector<DisplayState> displayStates;
     uint32_t flags = 0;
 
-    mForceSynchronous |= synchronous;
-
     for (auto const& kv : mComposerStates){
         composerStates.add(kv.second);
     }
 
     displayStates = std::move(mDisplayStates);
 
-    if (mForceSynchronous) {
+    if (synchronous) {
         flags |= ISurfaceComposer::eSynchronous;
     }
     if (mAnimation) {
         flags |= ISurfaceComposer::eAnimation;
     }
     if (oneWay) {
-      if (mForceSynchronous) {
-          ALOGE("Transaction attempted to set synchronous and one way at the same time"
-                " this is an invalid request. Synchronous will win for safety");
-      } else {
-          flags |= ISurfaceComposer::eOneWay;
-      }
+        if (synchronous) {
+            ALOGE("Transaction attempted to set synchronous and one way at the same time"
+                  " this is an invalid request. Synchronous will win for safety");
+        } else {
+            flags |= ISurfaceComposer::eOneWay;
+        }
     }
 
     // If both mEarlyWakeupStart and mEarlyWakeupEnd are set
@@ -2021,7 +2014,6 @@
     s.layerStackSpaceRect = layerStackRect;
     s.orientedDisplaySpaceRect = displayRect;
     s.what |= DisplayState::eDisplayProjectionChanged;
-    mForceSynchronous = true; // TODO: do we actually still need this?
 }
 
 void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height) {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 963cc09..61d4714 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -403,7 +403,6 @@
 
         uint64_t mId;
 
-        uint32_t mForceSynchronous = 0;
         uint32_t mTransactionNestCount = 0;
         bool mAnimation = false;
         bool mEarlyWakeupStart = false;
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index fbfecf6..3503a9e 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -61,6 +61,9 @@
 
     // Android O
     first_version: "26",
+    export_header_libs: [
+        "libnativewindow_ndk_headers",
+    ],
 }
 
 cc_library {
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 2237d2d..62cf255 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -12,7 +12,10 @@
     name: "libETC1",
     srcs: ["ETC1/etc1.cpp"],
     host_supported: true,
-    cflags: ["-Wall", "-Werror"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     target: {
         android: {
@@ -37,6 +40,9 @@
     symbol_file: "libEGL.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libEGL_headers",
+    ],
 }
 
 ndk_library {
@@ -44,6 +50,9 @@
     symbol_file: "libGLESv1_CM.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv1_CM_headers",
+    ],
 }
 
 ndk_library {
@@ -51,6 +60,9 @@
     symbol_file: "libGLESv2.map.txt",
     first_version: "9",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv2_headers",
+    ],
 }
 
 ndk_library {
@@ -58,6 +70,9 @@
     symbol_file: "libGLESv3.map.txt",
     first_version: "18",
     unversioned_until: "current",
+    export_header_libs: [
+        "libGLESv3_headers",
+    ],
 }
 
 cc_defaults {
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 88a9acb..ddcd51f 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -85,12 +85,12 @@
         "libstatspull",
         "libstatssocket",
         "libutils",
-        "libui",
         "server_configurable_flags",
     ],
     static_libs: [
         "libattestation",
         "libpalmrejection",
+        "libui-types",
     ],
 }
 
@@ -139,6 +139,7 @@
         "InputListener.cpp",
         "InputReaderBase.cpp",
         "InputThread.cpp",
+        "NotifyArgs.cpp",
         "VibrationElement.cpp",
     ],
 }
@@ -152,7 +153,6 @@
         "libcutils",
         "libinput",
         "liblog",
-        "libui",
         "libutils",
     ],
     header_libs: [
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 54d0e02..0972e22 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -31,206 +31,6 @@
 
 namespace android {
 
-// --- NotifyConfigurationChangedArgs ---
-
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
-      : id(id), eventTime(eventTime) {}
-
-// --- NotifyKeyArgs ---
-
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                             uint32_t source, int32_t displayId, uint32_t policyFlags,
-                             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
-                             int32_t metaState, nsecs_t downTime)
-      : id(id),
-        eventTime(eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        flags(flags),
-        keyCode(keyCode),
-        scanCode(scanCode),
-        metaState(metaState),
-        downTime(downTime),
-        readTime(readTime) {}
-
-// --- NotifyMotionArgs ---
-
-NotifyMotionArgs::NotifyMotionArgs(
-        int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
-        int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
-        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
-        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
-        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames)
-      : id(id),
-        eventTime(eventTime),
-        deviceId(deviceId),
-        source(source),
-        displayId(displayId),
-        policyFlags(policyFlags),
-        action(action),
-        actionButton(actionButton),
-        flags(flags),
-        metaState(metaState),
-        buttonState(buttonState),
-        classification(classification),
-        edgeFlags(edgeFlags),
-        pointerCount(pointerCount),
-        xPrecision(xPrecision),
-        yPrecision(yPrecision),
-        xCursorPosition(xCursorPosition),
-        yCursorPosition(yCursorPosition),
-        downTime(downTime),
-        readTime(readTime),
-        videoFrames(videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        this->pointerProperties[i].copyFrom(pointerProperties[i]);
-        this->pointerCoords[i].copyFrom(pointerCoords[i]);
-    }
-}
-
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
-      : id(other.id),
-        eventTime(other.eventTime),
-        deviceId(other.deviceId),
-        source(other.source),
-        displayId(other.displayId),
-        policyFlags(other.policyFlags),
-        action(other.action),
-        actionButton(other.actionButton),
-        flags(other.flags),
-        metaState(other.metaState),
-        buttonState(other.buttonState),
-        classification(other.classification),
-        edgeFlags(other.edgeFlags),
-        pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision),
-        yPrecision(other.yPrecision),
-        xCursorPosition(other.xCursorPosition),
-        yCursorPosition(other.yCursorPosition),
-        downTime(other.downTime),
-        readTime(other.readTime),
-        videoFrames(other.videoFrames) {
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        pointerProperties[i].copyFrom(other.pointerProperties[i]);
-        pointerCoords[i].copyFrom(other.pointerCoords[i]);
-    }
-}
-
-static inline bool isCursorPositionEqual(float lhs, float rhs) {
-    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
-}
-
-bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
-    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
-            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
-            policyFlags == rhs.policyFlags && action == rhs.action &&
-            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
-            buttonState == rhs.buttonState && classification == rhs.classification &&
-            edgeFlags == rhs.edgeFlags &&
-            pointerCount == rhs.pointerCount
-            // PointerProperties and PointerCoords are compared separately below
-            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
-            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
-            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
-            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
-    if (!equal) {
-        return false;
-    }
-
-    for (size_t i = 0; i < pointerCount; i++) {
-        equal =
-                pointerProperties[i] == rhs.pointerProperties[i]
-                && pointerCoords[i] == rhs.pointerCoords[i];
-        if (!equal) {
-            return false;
-        }
-    }
-    return true;
-}
-
-std::string NotifyMotionArgs::dump() const {
-    std::string coords;
-    for (uint32_t i = 0; i < pointerCount; i++) {
-        if (!coords.empty()) {
-            coords += ", ";
-        }
-        coords += StringPrintf("{%" PRIu32 ": ", i);
-        coords +=
-                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
-                             pointerCoords[i].getX(), pointerCoords[i].getY(),
-                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
-        const int32_t toolType = pointerProperties[i].toolType;
-        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
-            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
-        }
-        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
-        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
-        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
-        if (major != 0 || minor != 0) {
-            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
-                                   orientation);
-        }
-        coords += "}";
-    }
-    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
-                        ", source=%s, action=%s, pointerCount=%" PRIu32
-                        " pointers=%s, flags=0x%08x)",
-                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
-                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
-                        flags);
-}
-
-// --- NotifySwitchArgs ---
-
-NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
-                                   uint32_t switchValues, uint32_t switchMask)
-      : id(id),
-        eventTime(eventTime),
-        policyFlags(policyFlags),
-        switchValues(switchValues),
-        switchMask(switchMask) {}
-
-// --- NotifySensorArgs ---
-
-NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                   InputDeviceSensorType sensorType,
-                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
-                                   nsecs_t hwTimestamp, std::vector<float> values)
-      : id(id),
-        eventTime(eventTime),
-        deviceId(deviceId),
-        source(source),
-        sensorType(sensorType),
-        accuracy(accuracy),
-        accuracyChanged(accuracyChanged),
-        hwTimestamp(hwTimestamp),
-        values(std::move(values)) {}
-
-// --- NotifyVibratorStateArgs ---
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
-                                                 bool isOn)
-      : id(id), eventTime(eventTime), deviceId(deviceId), isOn(isOn) {}
-
-NotifyVibratorStateArgs::NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other)
-      : id(other.id), eventTime(other.eventTime), deviceId(other.deviceId), isOn(other.isOn) {}
-
-// --- NotifyDeviceResetArgs ---
-
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
-      : id(id), eventTime(eventTime), deviceId(deviceId) {}
-
-// --- NotifyPointerCaptureChangedArgs ---
-
-NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
-        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
-      : id(id), eventTime(eventTime), request(request) {}
-
 // --- InputListenerInterface ---
 
 // Helper to std::visit with lambdas.
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
new file mode 100644
index 0000000..1f37774
--- /dev/null
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NotifyArgs"
+
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include "NotifyArgs.h"
+
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+
+// --- NotifyConfigurationChangedArgs ---
+
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
+      : id(id), eventTime(eventTime) {}
+
+// --- NotifyKeyArgs ---
+
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                             uint32_t source, int32_t displayId, uint32_t policyFlags,
+                             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+                             int32_t metaState, nsecs_t downTime)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        flags(flags),
+        keyCode(keyCode),
+        scanCode(scanCode),
+        metaState(metaState),
+        downTime(downTime),
+        readTime(readTime) {}
+
+// --- NotifyMotionArgs ---
+
+NotifyMotionArgs::NotifyMotionArgs(
+        int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
+        int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
+        pointerCount(pointerCount),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
+        readTime(readTime),
+        videoFrames(videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        this->pointerProperties[i].copyFrom(pointerProperties[i]);
+        this->pointerCoords[i].copyFrom(pointerCoords[i]);
+    }
+}
+
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
+      : id(other.id),
+        eventTime(other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        displayId(other.displayId),
+        policyFlags(other.policyFlags),
+        action(other.action),
+        actionButton(other.actionButton),
+        flags(other.flags),
+        metaState(other.metaState),
+        buttonState(other.buttonState),
+        classification(other.classification),
+        edgeFlags(other.edgeFlags),
+        pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision),
+        yPrecision(other.yPrecision),
+        xCursorPosition(other.xCursorPosition),
+        yCursorPosition(other.yCursorPosition),
+        downTime(other.downTime),
+        readTime(other.readTime),
+        videoFrames(other.videoFrames) {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].copyFrom(other.pointerProperties[i]);
+        pointerCoords[i].copyFrom(other.pointerCoords[i]);
+    }
+}
+
+static inline bool isCursorPositionEqual(float lhs, float rhs) {
+    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
+}
+
+bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
+    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+            policyFlags == rhs.policyFlags && action == rhs.action &&
+            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+            buttonState == rhs.buttonState && classification == rhs.classification &&
+            edgeFlags == rhs.edgeFlags &&
+            pointerCount == rhs.pointerCount
+            // PointerProperties and PointerCoords are compared separately below
+            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
+            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
+            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
+    if (!equal) {
+        return false;
+    }
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        equal = pointerProperties[i] == rhs.pointerProperties[i] &&
+                pointerCoords[i] == rhs.pointerCoords[i];
+        if (!equal) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::string NotifyMotionArgs::dump() const {
+    std::string coords;
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (!coords.empty()) {
+            coords += ", ";
+        }
+        coords += StringPrintf("{%" PRIu32 ": ", i);
+        coords +=
+                StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id,
+                             pointerCoords[i].getX(), pointerCoords[i].getY(),
+                             pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+        const int32_t toolType = pointerProperties[i].toolType;
+        if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) {
+            coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType));
+        }
+        const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
+        const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
+        const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        if (major != 0 || minor != 0) {
+            coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor,
+                                   orientation);
+        }
+        coords += "}";
+    }
+    return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32
+                        ", source=%s, action=%s, pointerCount=%" PRIu32
+                        " pointers=%s, flags=0x%08x)",
+                        id, eventTime, deviceId, inputEventSourceToString(source).c_str(),
+                        MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(),
+                        flags);
+}
+
+// --- NotifySwitchArgs ---
+
+NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
+                                   uint32_t switchValues, uint32_t switchMask)
+      : id(id),
+        eventTime(eventTime),
+        policyFlags(policyFlags),
+        switchValues(switchValues),
+        switchMask(switchMask) {}
+
+// --- NotifySensorArgs ---
+
+NotifySensorArgs::NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                   InputDeviceSensorType sensorType,
+                                   InputDeviceSensorAccuracy accuracy, bool accuracyChanged,
+                                   nsecs_t hwTimestamp, std::vector<float> values)
+      : id(id),
+        eventTime(eventTime),
+        deviceId(deviceId),
+        source(source),
+        sensorType(sensorType),
+        accuracy(accuracy),
+        accuracyChanged(accuracyChanged),
+        hwTimestamp(hwTimestamp),
+        values(std::move(values)) {}
+
+// --- NotifyVibratorStateArgs ---
+
+NotifyVibratorStateArgs::NotifyVibratorStateArgs(int32_t id, nsecs_t eventTime, int32_t deviceId,
+                                                 bool isOn)
+      : id(id), eventTime(eventTime), deviceId(deviceId), isOn(isOn) {}
+
+// --- NotifyDeviceResetArgs ---
+
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
+      : id(id), eventTime(eventTime), deviceId(deviceId) {}
+
+// --- NotifyPointerCaptureChangedArgs ---
+
+NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(
+        int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request)
+      : id(id), eventTime(eventTime), request(request) {}
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 75071d5..e5c19af 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -26,7 +26,6 @@
         "libinputreporter",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
     ],
     static_libs: [
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 12eeea6..3d4dd7e 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -112,8 +112,6 @@
 
     void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
 
-    bool isPerDisplayTouchModeEnabled() override { return false; }
-
     InputDispatcherConfiguration mConfig;
 };
 
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index cdad9c9..eb79b76 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -63,7 +63,6 @@
         "libstatslog",
         "libstatspull",
         "libstatssocket",
-        "libui",
         "libgui",
         "libutils",
         "server_configurable_flags",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 8046bbe..33e7e17 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -195,9 +195,10 @@
 
 // --- TouchModeEntry ---
 
-TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode)
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
       : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
-        inTouchMode(inTouchMode) {}
+        inTouchMode(inTouchMode),
+        displayId(displayId) {}
 
 TouchModeEntry::~TouchModeEntry() {}
 
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 1f916f3..60f319a 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -210,8 +210,9 @@
 
 struct TouchModeEntry : EventEntry {
     bool inTouchMode;
+    int32_t displayId;
 
-    TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode);
+    TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
     std::string getDescription() const override;
 
     ~TouchModeEntry() override;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ff63a6f..399153c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -544,17 +544,12 @@
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
-        // mInTouchMode will be initialized by the WindowManager to the default device config.
-        // To avoid leaking stack in case that call never comes, and for tests,
-        // initialize it here anyways.
-        mInTouchMode(kDefaultInTouchMode),
         mMaximumObscuringOpacityForTouch(1.0f),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mWindowTokenWithPointerCapture(nullptr),
         mStaleEventTimeout(staleEventTimeout),
         mLatencyAggregator(),
-        mLatencyTracker(&mLatencyAggregator),
-        kPerDisplayTouchModeEnabled(mPolicy->isPerDisplayTouchModeEnabled()) {
+        mLatencyTracker(&mLatencyAggregator) {
     mLooper = sp<Looper>::make(false);
     mReporter = createInputReporter();
 
@@ -562,7 +557,6 @@
     SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
 
     mKeyRepeatState.lastKeyEntry = nullptr;
-
     policy->getDispatcherConfiguration(&mConfig);
 }
 
@@ -1435,7 +1429,7 @@
 void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
                                                     const std::shared_ptr<TouchModeEntry>& entry) {
     const std::vector<sp<WindowInfoHandle>>& windowHandles =
-            getWindowHandlesLocked(mFocusedDisplayId);
+            getWindowHandlesLocked(entry->displayId);
     if (windowHandles.empty()) {
         return;
     }
@@ -1452,7 +1446,6 @@
         const std::vector<sp<WindowInfoHandle>>& windowHandles) const {
     std::vector<InputTarget> inputTargets;
     for (const sp<WindowInfoHandle>& handle : windowHandles) {
-        // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only.
         const sp<IBinder>& token = handle->getToken();
         if (token == nullptr) {
             continue;
@@ -5012,15 +5005,20 @@
     bool needWake = false;
     {
         std::scoped_lock lock(mLock);
-        if (mInTouchMode == inTouchMode) {
+        ALOGD_IF(DEBUG_TOUCH_MODE,
+                 "Request to change touch mode to %s (calling pid=%d, uid=%d, "
+                 "hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)",
+                 toString(inTouchMode), pid, uid, toString(hasPermission), displayId,
+                 mTouchModePerDisplay.count(displayId) == 0
+                         ? "not set"
+                         : std::to_string(mTouchModePerDisplay[displayId]).c_str());
+
+        // TODO(b/198499018): Ensure that WM can guarantee that touch mode is properly set when
+        // display is created.
+        auto touchModeIt = mTouchModePerDisplay.find(displayId);
+        if (touchModeIt != mTouchModePerDisplay.end() && touchModeIt->second == inTouchMode) {
             return false;
         }
-        if (DEBUG_TOUCH_MODE) {
-            ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, "
-                  "hasPermission=%s, target displayId=%d, perDisplayTouchModeEnabled=%s)",
-                  toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission),
-                  displayId, toString(kPerDisplayTouchModeEnabled));
-        }
         if (!hasPermission) {
             if (!focusedWindowIsOwnedByLocked(pid, uid) &&
                 !recentWindowsAreOwnedByLocked(pid, uid)) {
@@ -5030,11 +5028,9 @@
                 return false;
             }
         }
-
-        // TODO(b/198499018): Store touch mode per display (kPerDisplayTouchModeEnabled)
-        mInTouchMode = inTouchMode;
-
-        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
+        mTouchModePerDisplay[displayId] = inTouchMode;
+        auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode,
+                                                      displayId);
         needWake = enqueueInboundEventLocked(std::move(entry));
     } // release lock
 
@@ -5474,6 +5470,16 @@
         dump += INDENT "AppSwitch: not pending\n";
     }
 
+    if (!mTouchModePerDisplay.empty()) {
+        dump += INDENT "TouchModePerDisplay:\n";
+        for (const auto& [displayId, touchMode] : mTouchModePerDisplay) {
+            dump += StringPrintf(INDENT2 "Display: %" PRId32 " TouchMode: %s\n", displayId,
+                                 std::to_string(touchMode).c_str());
+        }
+    } else {
+        dump += INDENT "TouchModePerDisplay: <none>\n";
+    }
+
     dump += INDENT "Configuration:\n";
     dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
     dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
@@ -6366,6 +6372,8 @@
         mFocusResolver.displayRemoved(displayId);
         // Reset pointer capture eligibility, regardless of previous state.
         std::erase(mIneligibleDisplaysForPointerCapture, displayId);
+        // Remove the associated touch mode state.
+        mTouchModePerDisplay.erase(displayId);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index b5bbce8..630d21a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -41,7 +41,6 @@
 #include <input/InputTransport.h>
 #include <limits.h>
 #include <stddef.h>
-#include <ui/Region.h>
 #include <unistd.h>
 #include <utils/BitSet.h>
 #include <utils/Looper.h>
@@ -343,9 +342,13 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
-    bool mInTouchMode GUARDED_BY(mLock);
     float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
 
+    // This map is not really needed, but it helps a lot with debugging (dumpsys input).
+    // In the java layer, touch mode states are spread across multiple DisplayContent objects,
+    // making harder to snapshot and retrieve them.
+    std::map<int32_t /*displayId*/, bool /*inTouchMode*/> mTouchModePerDisplay GUARDED_BY(mLock);
+
     class DispatcherWindowListener : public gui::WindowInfosListener {
     public:
         explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
@@ -384,7 +387,7 @@
     bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
             REQUIRES(mLock);
 
-    // Gets all the input targets (with their respective input channels) from the window handles
+    // Returns all the input targets (with their respective input channels) from the window handles
     // passed as argument.
     std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked(
             const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const
@@ -685,9 +688,6 @@
     bool focusedWindowIsOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
     bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock);
 
-    // Per display touch mode enabled
-    const bool kPerDisplayTouchModeEnabled;
-
     sp<InputReporterInterface> mReporter;
 };
 
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 44f3baf..7843923 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -136,9 +136,6 @@
 
     /* Notifies the policy that the drag window has moved over to another window */
     virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;
-
-    /* If touch mode is enabled per display or global */
-    virtual bool isPerDisplayTouchModeEnabled() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 036d57e..8647bcb 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -21,200 +21,10 @@
 #include <input/Input.h>
 #include <input/InputDevice.h>
 #include <input/TouchVideoFrame.h>
+#include "NotifyArgs.h"
 
 namespace android {
 
-class InputListenerInterface;
-
-/* Describes a configuration change event. */
-struct NotifyConfigurationChangedArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    inline NotifyConfigurationChangedArgs() { }
-
-    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
-
-    bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
-
-    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
-};
-
-/* Describes a key event. */
-struct NotifyKeyArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    int32_t deviceId;
-    uint32_t source;
-    int32_t displayId;
-    uint32_t policyFlags;
-    int32_t action;
-    int32_t flags;
-    int32_t keyCode;
-    int32_t scanCode;
-    int32_t metaState;
-    nsecs_t downTime;
-    nsecs_t readTime;
-
-    inline NotifyKeyArgs() { }
-
-    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                  uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
-                  int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
-                  nsecs_t downTime);
-
-    bool operator==(const NotifyKeyArgs& rhs) const = default;
-
-    NotifyKeyArgs(const NotifyKeyArgs& other) = default;
-};
-
-/* Describes a motion event. */
-struct NotifyMotionArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    int32_t deviceId;
-    uint32_t source;
-    int32_t displayId;
-    uint32_t policyFlags;
-    int32_t action;
-    int32_t actionButton;
-    int32_t flags;
-    int32_t metaState;
-    int32_t buttonState;
-    /**
-     * Classification of the current touch gesture
-     */
-    MotionClassification classification;
-    int32_t edgeFlags;
-
-    uint32_t pointerCount;
-    PointerProperties pointerProperties[MAX_POINTERS];
-    PointerCoords pointerCoords[MAX_POINTERS];
-    float xPrecision;
-    float yPrecision;
-    /**
-     * Mouse cursor position when this event is reported relative to the origin of the specified
-     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
-     * gestures enabled mode.
-     */
-    float xCursorPosition;
-    float yCursorPosition;
-    nsecs_t downTime;
-    nsecs_t readTime;
-    std::vector<TouchVideoFrame> videoFrames;
-
-    inline NotifyMotionArgs() { }
-
-    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
-                     uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
-                     int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-                     MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
-                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-                     float xPrecision, float yPrecision, float xCursorPosition,
-                     float yCursorPosition, nsecs_t downTime,
-                     const std::vector<TouchVideoFrame>& videoFrames);
-
-    NotifyMotionArgs(const NotifyMotionArgs& other);
-
-    bool operator==(const NotifyMotionArgs& rhs) const;
-
-    std::string dump() const;
-};
-
-/* Describes a sensor event. */
-struct NotifySensorArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    int32_t deviceId;
-    uint32_t source;
-    InputDeviceSensorType sensorType;
-    InputDeviceSensorAccuracy accuracy;
-    bool accuracyChanged;
-    nsecs_t hwTimestamp;
-    std::vector<float> values;
-
-    inline NotifySensorArgs() {}
-
-    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
-                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
-
-    NotifySensorArgs(const NotifySensorArgs& other) = default;
-};
-
-/* Describes a switch event. */
-struct NotifySwitchArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    uint32_t policyFlags;
-    uint32_t switchValues;
-    uint32_t switchMask;
-
-    inline NotifySwitchArgs() { }
-
-    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
-                     uint32_t switchMask);
-
-    NotifySwitchArgs(const NotifySwitchArgs& other) = default;
-
-    bool operator==(const NotifySwitchArgs& rhs) const = default;
-};
-
-/* Describes a device reset event, such as when a device is added,
- * reconfigured, or removed. */
-struct NotifyDeviceResetArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    int32_t deviceId;
-
-    inline NotifyDeviceResetArgs() { }
-
-    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
-
-    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default;
-
-    bool operator==(const NotifyDeviceResetArgs& rhs) const = default;
-};
-
-/* Describes a change in the state of Pointer Capture. */
-struct NotifyPointerCaptureChangedArgs {
-    // The sequence number of the Pointer Capture request, if enabled.
-    int32_t id;
-    nsecs_t eventTime;
-
-    PointerCaptureRequest request;
-
-    inline NotifyPointerCaptureChangedArgs() {}
-
-    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
-
-    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default;
-};
-
-/* Describes a vibrator state event. */
-struct NotifyVibratorStateArgs {
-    int32_t id;
-    nsecs_t eventTime;
-
-    int32_t deviceId;
-    bool isOn;
-
-    inline NotifyVibratorStateArgs() {}
-
-    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
-
-    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other);
-};
-
-using NotifyArgs = std::variant<NotifyConfigurationChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
-                                NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
-                                NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
-
 /*
  * The interface used by the InputReader to notify the InputListener about input events.
  */
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
new file mode 100644
index 0000000..611b1aa
--- /dev/null
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -0,0 +1,216 @@
+/*
+ * 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 <vector>
+
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/TouchVideoFrame.h>
+
+namespace android {
+
+/* Describes a configuration change event. */
+struct NotifyConfigurationChangedArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    inline NotifyConfigurationChangedArgs() {}
+
+    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
+
+    bool operator==(const NotifyConfigurationChangedArgs& rhs) const = default;
+
+    NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other) = default;
+};
+
+/* Describes a key event. */
+struct NotifyKeyArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    nsecs_t downTime;
+    nsecs_t readTime;
+
+    inline NotifyKeyArgs() {}
+
+    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                  uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                  int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+                  nsecs_t downTime);
+
+    bool operator==(const NotifyKeyArgs& rhs) const = default;
+
+    NotifyKeyArgs(const NotifyKeyArgs& other) = default;
+};
+
+/* Describes a motion event. */
+struct NotifyMotionArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    int32_t displayId;
+    uint32_t policyFlags;
+    int32_t action;
+    int32_t actionButton;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+    /**
+     * Classification of the current touch gesture
+     */
+    MotionClassification classification;
+    int32_t edgeFlags;
+
+    uint32_t pointerCount;
+    PointerProperties pointerProperties[MAX_POINTERS];
+    PointerCoords pointerCoords[MAX_POINTERS];
+    float xPrecision;
+    float yPrecision;
+    /**
+     * Mouse cursor position when this event is reported relative to the origin of the specified
+     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
+     * gestures enabled mode.
+     */
+    float xCursorPosition;
+    float yCursorPosition;
+    nsecs_t downTime;
+    nsecs_t readTime;
+    std::vector<TouchVideoFrame> videoFrames;
+
+    inline NotifyMotionArgs() {}
+
+    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                     uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                     int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
+                     MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
+                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+                     float xPrecision, float yPrecision, float xCursorPosition,
+                     float yCursorPosition, nsecs_t downTime,
+                     const std::vector<TouchVideoFrame>& videoFrames);
+
+    NotifyMotionArgs(const NotifyMotionArgs& other);
+
+    bool operator==(const NotifyMotionArgs& rhs) const;
+
+    std::string dump() const;
+};
+
+/* Describes a sensor event. */
+struct NotifySensorArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    uint32_t source;
+    InputDeviceSensorType sensorType;
+    InputDeviceSensorAccuracy accuracy;
+    bool accuracyChanged;
+    nsecs_t hwTimestamp;
+    std::vector<float> values;
+
+    inline NotifySensorArgs() {}
+
+    NotifySensorArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                     InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy,
+                     bool accuracyChanged, nsecs_t hwTimestamp, std::vector<float> values);
+
+    NotifySensorArgs(const NotifySensorArgs& other) = default;
+};
+
+/* Describes a switch event. */
+struct NotifySwitchArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    uint32_t policyFlags;
+    uint32_t switchValues;
+    uint32_t switchMask;
+
+    inline NotifySwitchArgs() {}
+
+    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
+                     uint32_t switchMask);
+
+    NotifySwitchArgs(const NotifySwitchArgs& other) = default;
+
+    bool operator==(const NotifySwitchArgs& rhs) const = default;
+};
+
+/* Describes a device reset event, such as when a device is added,
+ * reconfigured, or removed. */
+struct NotifyDeviceResetArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+
+    inline NotifyDeviceResetArgs() {}
+
+    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
+
+    NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) = default;
+
+    bool operator==(const NotifyDeviceResetArgs& rhs) const = default;
+};
+
+/* Describes a change in the state of Pointer Capture. */
+struct NotifyPointerCaptureChangedArgs {
+    // The sequence number of the Pointer Capture request, if enabled.
+    int32_t id;
+    nsecs_t eventTime;
+
+    PointerCaptureRequest request;
+
+    inline NotifyPointerCaptureChangedArgs() {}
+
+    NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&);
+
+    NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other) = default;
+};
+
+/* Describes a vibrator state event. */
+struct NotifyVibratorStateArgs {
+    int32_t id;
+    nsecs_t eventTime;
+
+    int32_t deviceId;
+    bool isOn;
+
+    inline NotifyVibratorStateArgs() {}
+
+    NotifyVibratorStateArgs(int32_t id, nsecs_t eventTIme, int32_t deviceId, bool isOn);
+
+    NotifyVibratorStateArgs(const NotifyVibratorStateArgs& other) = default;
+};
+
+using NotifyArgs = std::variant<NotifyConfigurationChangedArgs, NotifyKeyArgs, NotifyMotionArgs,
+                                NotifySensorArgs, NotifySwitchArgs, NotifyDeviceResetArgs,
+                                NotifyPointerCaptureChangedArgs, NotifyVibratorStateArgs>;
+
+} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 01146a3..0f87201 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -69,12 +69,12 @@
         "libinput",
         "liblog",
         "libstatslog",
-        "libui",
         "libutils",
         "libPlatformProperties",
     ],
     static_libs: [
         "libc++fs",
+        "libui-types",
     ],
     header_libs: [
         "libbatteryservice_headers",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index c9d21dc..ca7e426 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -76,6 +76,8 @@
 static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
 static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
 
+static constexpr size_t EVENT_BUFFER_SIZE = 256;
+
 // Mapping for input battery class node IDs lookup.
 // https://www.kernel.org/doc/Documentation/power/power_supply_class.txt
 static const std::unordered_map<std::string, InputBatteryClass> BATTERY_CLASSES =
@@ -117,7 +119,8 @@
          {"brightness", InputLightClass::BRIGHTNESS},
          {"multi_index", InputLightClass::MULTI_INDEX},
          {"multi_intensity", InputLightClass::MULTI_INTENSITY},
-         {"max_brightness", InputLightClass::MAX_BRIGHTNESS}};
+         {"max_brightness", InputLightClass::MAX_BRIGHTNESS},
+         {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}};
 
 // Mapping for input multicolor led class node names.
 // https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
@@ -365,15 +368,26 @@
         info.path = nodePath;
         info.name = nodePath.filename();
         info.maxBrightness = std::nullopt;
-        size_t nameStart = info.name.rfind(":");
-        if (nameStart != std::string::npos) {
-            // Trim the name to color name
-            info.name = info.name.substr(nameStart + 1);
-            // Set InputLightClass flag for colors
-            const auto it = LIGHT_CLASSES.find(info.name);
-            if (it != LIGHT_CLASSES.end()) {
-                info.flags |= it->second;
+
+        // Light name should follow the naming pattern <name>:<color>:<function>
+        // Refer kernel docs /leds/leds-class.html for valid supported LED names.
+        std::regex indexPattern("([a-zA-Z0-9_.:]*:)?([a-zA-Z0-9_.]*):([a-zA-Z0-9_.]*)");
+        std::smatch results;
+
+        if (std::regex_match(info.name, results, indexPattern)) {
+            // regex_match will return full match at index 0 and <name> at index 1. For RawLightInfo
+            // we only care about sections <color> and <function> which will be at index 2 and 3.
+            for (int i = 2; i <= 3; i++) {
+                const auto it = LIGHT_CLASSES.find(results.str(i));
+                if (it != LIGHT_CLASSES.end()) {
+                    info.flags |= it->second;
+                }
             }
+
+            // Set name of the raw light to <function> which represents playerIDs for LEDs that
+            // turn on/off based on the current player ID (Refer to PeripheralController.cpp for
+            // player ID logic)
+            info.name = results.str(3);
         }
         // Scan the path for all the files
         // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt
@@ -1404,17 +1418,28 @@
     }
 
     const auto& path = *sysfsRootPathOpt;
-    for (const auto& [id, dev] : mDevices) {
-        if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
-            return dev->associatedDevice;
-        }
-    }
 
-    return std::make_shared<AssociatedDevice>(
+    std::shared_ptr<const AssociatedDevice> associatedDevice = std::make_shared<AssociatedDevice>(
             AssociatedDevice{.sysfsRootPath = path,
                              .countryCode = readCountryCodeLocked(path),
                              .batteryInfos = readBatteryConfiguration(path),
                              .lightInfos = readLightsConfiguration(path)});
+
+    bool associatedDeviceChanged = false;
+    for (const auto& [id, dev] : mDevices) {
+        if (dev->associatedDevice && dev->associatedDevice->sysfsRootPath == path) {
+            if (*associatedDevice != *dev->associatedDevice) {
+                associatedDeviceChanged = true;
+                dev->associatedDevice = associatedDevice;
+            }
+            associatedDevice = dev->associatedDevice;
+        }
+    }
+    ALOGI_IF(associatedDeviceChanged,
+             "The AssociatedDevice changed for path '%s'. Using new AssociatedDevice: %s",
+             path.c_str(), associatedDevice->dump().c_str());
+
+    return associatedDevice;
 }
 
 void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) {
@@ -1621,15 +1646,12 @@
     return std::nullopt;
 }
 
-size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
-    ALOG_ASSERT(bufferSize >= 1);
-
+std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) {
     std::scoped_lock _l(mLock);
 
-    struct input_event readBuffer[bufferSize];
+    std::array<input_event, EVENT_BUFFER_SIZE> readBuffer;
 
-    RawEvent* event = buffer;
-    size_t capacity = bufferSize;
+    std::vector<RawEvent> events;
     bool awoken = false;
     for (;;) {
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -1649,15 +1671,17 @@
         for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) {
             std::unique_ptr<Device> device = std::move(*it);
             ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = (device->id == mBuiltInKeyboardId)
+            const int32_t deviceId = (device->id == mBuiltInKeyboardId)
                     ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                     : device->id;
-            event->type = DEVICE_REMOVED;
-            event += 1;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_REMOVED,
+            });
             it = mClosingDevices.erase(it);
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1672,10 +1696,12 @@
             std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
             mOpeningDevices.pop_back();
             ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
-            event->when = now;
-            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
-            event->type = DEVICE_ADDED;
-            event += 1;
+            const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+            events.push_back({
+                    .when = now,
+                    .deviceId = deviceId,
+                    .type = DEVICE_ADDED,
+            });
 
             // Try to find a matching video device by comparing device names
             for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
@@ -1693,17 +1719,18 @@
                 ALOGW("Device id %d exists, replaced.", device->id);
             }
             mNeedToSendFinishedDeviceScan = true;
-            if (--capacity == 0) {
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
 
         if (mNeedToSendFinishedDeviceScan) {
             mNeedToSendFinishedDeviceScan = false;
-            event->when = now;
-            event->type = FINISHED_DEVICE_SCAN;
-            event += 1;
-            if (--capacity == 0) {
+            events.push_back({
+                    .when = now,
+                    .type = FINISHED_DEVICE_SCAN,
+            });
+            if (events.size() == EVENT_BUFFER_SIZE) {
                 break;
             }
         }
@@ -1767,12 +1794,13 @@
             // This must be an input event
             if (eventItem.events & EPOLLIN) {
                 int32_t readSize =
-                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
+                        read(device->fd, readBuffer.data(),
+                             sizeof(decltype(readBuffer)::value_type) * readBuffer.size());
                 if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                     // Device was removed before INotify noticed.
                     ALOGW("could not get event, removed? (fd: %d size: %" PRId32
-                          " bufferSize: %zu capacity: %zu errno: %d)\n",
-                          device->fd, readSize, bufferSize, capacity, errno);
+                          " capacity: %zu errno: %d)\n",
+                          device->fd, readSize, readBuffer.size(), errno);
                     deviceChanged = true;
                     closeDeviceLocked(*device);
                 } else if (readSize < 0) {
@@ -1782,21 +1810,21 @@
                 } else if ((readSize % sizeof(struct input_event)) != 0) {
                     ALOGE("could not get event (wrong size: %d)", readSize);
                 } else {
-                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
+                    const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
-                    size_t count = size_t(readSize) / sizeof(struct input_event);
+                    const size_t count = size_t(readSize) / sizeof(struct input_event);
                     for (size_t i = 0; i < count; i++) {
                         struct input_event& iev = readBuffer[i];
-                        event->when = processEventTimestamp(iev);
-                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                        event->deviceId = deviceId;
-                        event->type = iev.type;
-                        event->code = iev.code;
-                        event->value = iev.value;
-                        event += 1;
-                        capacity -= 1;
+                        events.push_back({
+                                .when = processEventTimestamp(iev),
+                                .readTime = systemTime(SYSTEM_TIME_MONOTONIC),
+                                .deviceId = deviceId,
+                                .type = iev.type,
+                                .code = iev.code,
+                                .value = iev.value,
+                        });
                     }
-                    if (capacity == 0) {
+                    if (events.size() >= EVENT_BUFFER_SIZE) {
                         // The result buffer is full.  Reset the pending event index
                         // so we will try to read the device again on the next iteration.
                         mPendingEventIndex -= 1;
@@ -1832,7 +1860,7 @@
         }
 
         // Return now if we have collected any events or if we were explicitly awoken.
-        if (event != buffer || awoken) {
+        if (!events.empty() || awoken) {
             break;
         }
 
@@ -1878,7 +1906,7 @@
     }
 
     // All done, return the number of events we read.
-    return event - buffer;
+    return events;
 }
 
 std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) {
@@ -2633,4 +2661,9 @@
     std::unique_lock<std::mutex> lock(mLock);
 }
 
+std::string EventHub::AssociatedDevice::dump() const {
+    return StringPrintf("path=%s, numBatteries=%zu, numLight=%zu", sysfsRootPath.c_str(),
+                        batteryInfos.size(), lightInfos.size());
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 44a005e..6b9b9f1 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -234,6 +234,10 @@
 }
 
 void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    if (mController != nullptr && mController->getEventHubId() == eventHubId) {
+        // Delete mController, since the corresponding eventhub device is going away
+        mController = nullptr;
+    }
     mDevices.erase(eventHubId);
 }
 
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 4c38ce8..8650876 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -120,14 +120,14 @@
         }
     } // release lock
 
-    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
+    std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);
 
     { // acquire lock
         std::scoped_lock _l(mLock);
         mReaderIsAliveCondition.notify_all();
 
-        if (count) {
-            processEventsLocked(mEventBuffer, count);
+        if (!events.empty()) {
+            processEventsLocked(events.data(), events.size());
         }
 
         if (mNextTimeout != LLONG_MAX) {
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index eaf5b51..cedbacb 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -16,6 +16,7 @@
 
 #include <locale>
 #include <regex>
+#include <set>
 
 #include <ftl/enum.h>
 
@@ -70,7 +71,7 @@
 
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = brightness * ratio;
@@ -89,7 +90,7 @@
     }
     // If the light node doesn't have max brightness, use the default max brightness.
     int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
-    float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
+    float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
     // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
     if (rawMaxBrightness != MAX_BRIGHTNESS) {
         brightness = ceil(brightness / ratio);
@@ -271,7 +272,8 @@
 
     for (const auto& [lightId, light] : mLights) {
         // Input device light doesn't support ordinal, always pass 1.
-        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
+        InputDeviceLightInfo lightInfo(light->name, light->id, light->type, light->capabilityFlags,
+                                       1 /* ordinal */);
         deviceInfo->addLightInfo(lightInfo);
     }
 }
@@ -284,6 +286,8 @@
             dump += StringPrintf(INDENT4 "Id: %d", lightId);
             dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
             dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
+            dump += StringPrintf(INDENT4 "Capability flags: %s",
+                                 light->capabilityFlags.string().c_str());
             light->dump(dump);
         }
     }
@@ -363,6 +367,8 @@
     std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
     // Map from player Id to raw light Id
     std::unordered_map<int32_t, int32_t> playerIdLightIds;
+    // Set of Keyboard backlights
+    std::set<int32_t> keyboardBacklightIds;
 
     // Check raw lights
     const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
@@ -391,6 +397,10 @@
                 }
             }
         }
+        // Check if this is a Keyboard backlight
+        if (rawInfo->flags.test(InputLightClass::KEYBOARD_BACKLIGHT)) {
+            keyboardBacklightIds.insert(rawId);
+        }
         // Check if this is an LED of RGB light
         if (rawInfo->flags.test(InputLightClass::RED)) {
             hasRedLed = true;
@@ -431,8 +441,21 @@
             ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
                   rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
         }
+        bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
+                        keyboardBacklightIds.end() &&
+                keyboardBacklightIds.find(rawRgbIds.at(LightColor::BLUE)) !=
+                        keyboardBacklightIds.end() &&
+                (!rawGlobalId.has_value() ||
+                 keyboardBacklightIds.find(rawGlobalId.value()) != keyboardBacklightIds.end());
+
         std::unique_ptr<Light> light =
-                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, rawRgbIds, rawGlobalId);
+                std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
+                                           isKeyboardBacklight
+                                                   ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                                                   : InputDeviceLightType::INPUT,
+                                           rawRgbIds, rawGlobalId);
         mLights.insert_or_assign(light->id, std::move(light));
         // Remove from raw light info as they've been composed a RBG light.
         rawInfos.erase(rawRgbIds.at(LightColor::RED));
@@ -445,6 +468,10 @@
 
     // Check the rest of raw light infos
     for (const auto& [rawId, rawInfo] : rawInfos) {
+        InputDeviceLightType type = keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()
+                ? InputDeviceLightType::KEYBOARD_BACKLIGHT
+                : InputDeviceLightType::INPUT;
+
         // If the node is multi-color led, construct a MULTI_COLOR light
         if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
             rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
@@ -453,7 +480,7 @@
             }
             std::unique_ptr<Light> light =
                     std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
-                                                      rawInfo.id);
+                                                      type, rawInfo.id);
             mLights.insert_or_assign(light->id, std::move(light));
             continue;
         }
@@ -462,7 +489,7 @@
             ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
         }
         std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
-                                                                   ++mNextId, rawInfo.id);
+                                                                   ++mNextId, type, rawInfo.id);
 
         mLights.insert_or_assign(light->id, std::move(light));
     }
diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h
index 25cf435..8ac42c3 100644
--- a/services/inputflinger/reader/controller/PeripheralController.h
+++ b/services/inputflinger/reader/controller/PeripheralController.h
@@ -67,6 +67,7 @@
         std::string name;
         int32_t id;
         InputDeviceLightType type;
+        ftl::Flags<InputDeviceLightCapability> capabilityFlags;
 
         virtual bool setLightColor(int32_t color) { return false; }
         virtual std::optional<int32_t> getLightColor() { return std::nullopt; }
@@ -81,8 +82,10 @@
 
     struct MonoLight : public Light {
         explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                           int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {}
+                           InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -91,15 +94,15 @@
     };
 
     struct RgbLight : public Light {
-        explicit RgbLight(InputDeviceContext& context, int32_t id,
+        explicit RgbLight(InputDeviceContext& context, int32_t id, InputDeviceLightType type,
                           const std::unordered_map<LightColor, int32_t>& rawRgbIds,
                           std::optional<int32_t> rawGlobalId)
-              : Light(context, "RGB", id, InputDeviceLightType::RGB),
-                rawRgbIds(rawRgbIds),
-                rawGlobalId(rawGlobalId) {
+              : Light(context, "RGB", id, type), rawRgbIds(rawRgbIds), rawGlobalId(rawGlobalId) {
             brightness = rawGlobalId.has_value()
                     ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS)
                     : MAX_BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
         }
         // Map from color to raw light id.
         std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
@@ -114,8 +117,11 @@
 
     struct MultiColorLight : public Light {
         explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
-                                 int32_t rawId)
-              : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
+                                 InputDeviceLightType type, int32_t rawId)
+              : Light(context, name, id, type), rawId(rawId) {
+            capabilityFlags |= InputDeviceLightCapability::BRIGHTNESS;
+            capabilityFlags |= InputDeviceLightCapability::RGB;
+        }
         int32_t rawId;
 
         bool setLightColor(int32_t color) override;
@@ -131,7 +137,7 @@
         // Map from player Id to raw light Id
         std::unordered_map<int32_t, int32_t> rawLightIds;
 
-        bool setLightPlayerId(int32_t palyerId) override;
+        bool setLightPlayerId(int32_t playerId) override;
         std::optional<int32_t> getLightPlayerId() override;
         void dump(std::string& dump) override;
     };
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 5cf2214..6933ec7 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -174,6 +174,8 @@
     MULTI_INTENSITY = 0x00000040,
     /* The input light has max brightness node. */
     MAX_BRIGHTNESS = 0x00000080,
+    /* The input light has kbd_backlight name */
+    KEYBOARD_BACKLIGHT = 0x00000100,
 };
 
 enum class InputBatteryClass : uint32_t {
@@ -193,6 +195,9 @@
     ftl::Flags<InputLightClass> flags;
     std::array<int32_t, COLOR_NUM> rgbIndex;
     std::filesystem::path path;
+
+    bool operator==(const RawLightInfo&) const = default;
+    bool operator!=(const RawLightInfo&) const = default;
 };
 
 /* Describes a raw battery. */
@@ -201,6 +206,9 @@
     std::string name;
     ftl::Flags<InputBatteryClass> flags;
     std::filesystem::path path;
+
+    bool operator==(const RawBatteryInfo&) const = default;
+    bool operator!=(const RawBatteryInfo&) const = default;
 };
 
 /*
@@ -280,7 +288,7 @@
      *
      * Returns the number of events obtained, or 0 if the timeout expired.
      */
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) = 0;
+    virtual std::vector<RawEvent> getEvents(int timeoutMillis) = 0;
     virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0;
     virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(
             int32_t deviceId, int32_t absCode) const = 0;
@@ -498,7 +506,7 @@
     bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
                                uint8_t* outFlags) const override final;
 
-    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final;
+    std::vector<RawEvent> getEvents(int timeoutMillis) override final;
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
 
     bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
@@ -549,6 +557,10 @@
         hardware::input::InputDeviceCountryCode countryCode;
         std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
+
+        bool operator==(const AssociatedDevice&) const = default;
+        bool operator!=(const AssociatedDevice&) const = default;
+        std::string dump() const;
     };
 
     struct Device {
@@ -582,7 +594,7 @@
 
         // A shared_ptr of a device associated with the input device.
         // The input devices that have the same sysfs path have the same associated device.
-        const std::shared_ptr<const AssociatedDevice> associatedDevice;
+        std::shared_ptr<const AssociatedDevice> associatedDevice;
 
         int32_t controllerNumber;
 
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index fbce87f..012d43f 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -170,10 +170,6 @@
 
     InputReaderConfiguration mConfig GUARDED_BY(mLock);
 
-    // The event queue.
-    static const int EVENT_BUFFER_SIZE = 256;
-    RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);
-
     // An input device can represent a collection of EventHub devices. This map provides a way
     // to lookup the input device instance from the EventHub device id.
     std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 6ef6e44..9380c71 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -99,8 +99,6 @@
 };
 
 std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) {
-    static constexpr size_t EVENT_BUFFER_SIZE = 256;
-    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
     std::vector<RawEvent> events;
 
     while (true) {
@@ -108,12 +106,12 @@
         if (expectedEvents) {
             timeout = 2s;
         }
-        const size_t count =
-                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
-        if (count == 0) {
+
+        std::vector<RawEvent> newEvents = mEventHub->getEvents(timeout.count());
+        if (newEvents.empty()) {
             break;
         }
-        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+        events.insert(events.end(), newEvents.begin(), newEvents.end());
         if (expectedEvents && events.size() >= *expectedEvents) {
             break;
         }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 7ee6950..985c9c5 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -63,10 +63,14 @@
 static constexpr int32_t POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-// The default pid and uid for windows created by the test.
+// The default pid and uid for windows created on the primary display by the test.
 static constexpr int32_t WINDOW_PID = 999;
 static constexpr int32_t WINDOW_UID = 1001;
 
+// The default pid and uid for the windows created on the secondary display by the test.
+static constexpr int32_t SECONDARY_WINDOW_PID = 1010;
+static constexpr int32_t SECONDARY_WINDOW_UID = 1012;
+
 // The default policy flags to use for event injection by tests.
 static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
 
@@ -500,11 +504,6 @@
         verify(*mFilteredEvent);
         mFilteredEvent = nullptr;
     }
-
-    bool isPerDisplayTouchModeEnabled() {
-        // TODO(b/198499018): Make this a regular property once per display touch mode is enabled
-        return false;
-    }
 };
 
 // --- InputDispatcherTest ---
@@ -5461,7 +5460,6 @@
                 sp<FakeWindowHandle>::make(mApplication, mDispatcher,
                                            "Window without input channel", ADISPLAY_ID_DEFAULT,
                                            std::make_optional<sp<IBinder>>(nullptr) /*token*/);
-
         mNoInputWindow->setNoInputChannel(true);
         mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
         // It's perfectly valid for this window to not have an associated input channel
@@ -6806,44 +6804,67 @@
 class InputDispatcherTouchModeChangedTests : public InputDispatcherTest {
 protected:
     std::shared_ptr<FakeApplicationHandle> mApp;
+    std::shared_ptr<FakeApplicationHandle> mSecondaryApp;
     sp<FakeWindowHandle> mWindow;
     sp<FakeWindowHandle> mSecondWindow;
+    sp<FakeWindowHandle> mThirdWindow;
 
     void SetUp() override {
         InputDispatcherTest::SetUp();
 
         mApp = std::make_shared<FakeApplicationHandle>();
+        mSecondaryApp = std::make_shared<FakeApplicationHandle>();
         mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
         mWindow->setFocusable(true);
         setFocusedWindow(mWindow);
         mSecondWindow =
                 sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
         mSecondWindow->setFocusable(true);
+        mThirdWindow =
+                sp<FakeWindowHandle>::make(mSecondaryApp, mDispatcher,
+                                           "TestWindow3_SecondaryDisplay", SECOND_DISPLAY_ID);
+        mThirdWindow->setFocusable(true);
 
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
-        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}},
+                                      {SECOND_DISPLAY_ID, {mThirdWindow}}});
+        mThirdWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID);
         mWindow->consumeFocusEvent(true);
 
-        // Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
+        // Set main display initial touch mode to InputDispatcher::kDefaultInTouchMode.
         if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
-                                        WINDOW_UID, true /*hasPermission*/, ADISPLAY_ID_DEFAULT)) {
+                                        WINDOW_UID, true /* hasPermission */,
+                                        ADISPLAY_ID_DEFAULT)) {
             mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
             mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
+            mThirdWindow->assertNoEvents();
+        }
+
+        // Set secondary display initial touch mode to InputDispatcher::kDefaultInTouchMode.
+        if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, SECONDARY_WINDOW_PID,
+                                        SECONDARY_WINDOW_UID, true /* hasPermission */,
+                                        SECOND_DISPLAY_ID)) {
+            mWindow->assertNoEvents();
+            mSecondWindow->assertNoEvents();
+            mThirdWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
         }
     }
 
-    void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) {
+    void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, int32_t pid, int32_t uid,
+                                                   bool hasPermission) {
         ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
                                                 ADISPLAY_ID_DEFAULT));
         mWindow->consumeTouchModeEvent(inTouchMode);
         mSecondWindow->consumeTouchModeEvent(inTouchMode);
+        mThirdWindow->assertNoEvents();
     }
 };
 
 TEST_F(InputDispatcherTouchModeChangedTests, FocusedWindowCanChangeTouchMode) {
     const WindowInfo& windowInfo = *mWindow->getInfo();
-    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid,
-                             windowInfo.ownerUid, false /*hasPermission*/);
+    changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode,
+                                              windowInfo.ownerPid, windowInfo.ownerUid,
+                                              false /*  hasPermission */);
 }
 
 TEST_F(InputDispatcherTouchModeChangedTests, NonFocusedWindowOwnerCannotChangeTouchMode) {
@@ -6863,8 +6884,8 @@
     int32_t ownerPid = windowInfo.ownerPid;
     int32_t ownerUid = windowInfo.ownerUid;
     mWindow->setOwnerInfo(/* pid */ -1, /* uid */ -1);
-    changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, ownerPid, ownerUid,
-                             true /*hasPermission*/);
+    changeAndVerifyTouchModeInMainDisplayOnly(!InputDispatcher::kDefaultInTouchMode, ownerPid,
+                                              ownerUid, true /*hasPermission*/);
 }
 
 TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
@@ -6876,6 +6897,16 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchOnSecondaryDisplayOnly) {
+    const WindowInfo& windowInfo = *mThirdWindow->getInfo();
+    ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode,
+                                            windowInfo.ownerPid, windowInfo.ownerUid,
+                                            true /*hasPermission*/, SECOND_DISPLAY_ID));
+    mWindow->assertNoEvents();
+    mSecondWindow->assertNoEvents();
+    mThirdWindow->consumeTouchModeEvent(!InputDispatcher::kDefaultInTouchMode);
+}
+
 TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
     // Interact with the window first.
     ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp
index 22ae894..ca548be 100644
--- a/services/inputflinger/tests/InputFlingerService_test.cpp
+++ b/services/inputflinger/tests/InputFlingerService_test.cpp
@@ -33,8 +33,6 @@
 #include <inttypes.h>
 #include <linux/uinput.h>
 #include <log/log.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
 #include <chrono>
 #include <thread>
 #include <unordered_map>
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 097659b..78ea692 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -826,15 +826,14 @@
         mExcludedDevices = devices;
     }
 
-    size_t getEvents(int, RawEvent* buffer, size_t bufferSize) override {
+    std::vector<RawEvent> getEvents(int) override {
         std::scoped_lock lock(mLock);
 
-        const size_t filledSize = std::min(mEvents.size(), bufferSize);
-        std::copy(mEvents.begin(), mEvents.begin() + filledSize, buffer);
+        std::vector<RawEvent> buffer;
+        std::swap(buffer, mEvents);
 
-        mEvents.erase(mEvents.begin(), mEvents.begin() + filledSize);
         mEventsCondition.notify_all();
-        return filledSize;
+        return buffer;
     }
 
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
@@ -2972,6 +2971,21 @@
     ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
 }
 
+/**
+ * This test reproduces a crash caused by a dangling reference that remains after device is added
+ * and removed. The reference is accessed in InputDevice::dump(..);
+ */
+TEST_F(InputDeviceTest, DumpDoesNotCrash) {
+    constexpr int32_t TEST_EVENTHUB_ID = 10;
+    mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
+
+    InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/);
+    device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/);
+    device.removeEventHubDevice(TEST_EVENTHUB_ID);
+    std::string dumpStr, eventHubDevStr;
+    device.dump(dumpStr, eventHubDevStr);
+}
+
 // --- InputMapperTest ---
 
 class InputMapperTest : public testing::Test {
@@ -10107,7 +10121,7 @@
 
 TEST_F(LightControllerTest, MonoLight) {
     RawLightInfo infoMono = {.id = 1,
-                             .name = "Mono",
+                             .name = "mono_light",
                              .maxBrightness = 255,
                              .flags = InputLightClass::BRIGHTNESS,
                              .path = ""};
@@ -10118,7 +10132,29 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MONO, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
+}
+
+TEST_F(LightControllerTest, MonoKeyboardBacklight) {
+    RawLightInfo infoMono = {.id = 1,
+                             .name = "mono_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_BRIGHTNESS));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
@@ -10149,7 +10185,85 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::RGB, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, CorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red_keyboard_backlight",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED |
+                                    InputLightClass::KEYBOARD_BACKLIGHT,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue_keyboard_backlight",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE |
+                                     InputLightClass::KEYBOARD_BACKLIGHT,
+                             .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, IncorrectRGBKeyboardBacklight) {
+    RawLightInfo infoRed = {.id = 1,
+                            .name = "red",
+                            .maxBrightness = 255,
+                            .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED,
+                            .path = ""};
+    RawLightInfo infoGreen = {.id = 2,
+                              .name = "green",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN,
+                              .path = ""};
+    RawLightInfo infoBlue = {.id = 3,
+                             .name = "blue",
+                             .maxBrightness = 255,
+                             .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE,
+                             .path = ""};
+    RawLightInfo infoGlobal = {.id = 3,
+                               .name = "global_keyboard_backlight",
+                               .maxBrightness = 255,
+                               .flags = InputLightClass::BRIGHTNESS | InputLightClass::GLOBAL |
+                                       InputLightClass::KEYBOARD_BACKLIGHT,
+                               .path = ""};
+    mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed));
+    mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue));
+    mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoGlobal));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10157,7 +10271,7 @@
 
 TEST_F(LightControllerTest, MultiColorRGBLight) {
     RawLightInfo infoColor = {.id = 1,
-                              .name = "red",
+                              .name = "multi_color",
                               .maxBrightness = 255,
                               .flags = InputLightClass::BRIGHTNESS |
                                       InputLightClass::MULTI_INTENSITY |
@@ -10171,7 +10285,34 @@
     controller.populateDeviceInfo(&info);
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
-    ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, lights[0].type);
+    ASSERT_EQ(InputDeviceLightType::INPUT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+    ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+    ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
+}
+
+TEST_F(LightControllerTest, MultiColorRGBKeyboardBacklight) {
+    RawLightInfo infoColor = {.id = 1,
+                              .name = "multi_color_keyboard_backlight",
+                              .maxBrightness = 255,
+                              .flags = InputLightClass::BRIGHTNESS |
+                                      InputLightClass::MULTI_INTENSITY |
+                                      InputLightClass::MULTI_INDEX |
+                                      InputLightClass::KEYBOARD_BACKLIGHT,
+                              .path = ""};
+
+    mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor));
+
+    PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+    InputDeviceInfo info;
+    controller.populateDeviceInfo(&info);
+    std::vector<InputDeviceLightInfo> lights = info.getLights();
+    ASSERT_EQ(1U, lights.size());
+    ASSERT_EQ(InputDeviceLightType::KEYBOARD_BACKLIGHT, lights[0].type);
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_TRUE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_TRUE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
@@ -10209,6 +10350,8 @@
     std::vector<InputDeviceLightInfo> lights = info.getLights();
     ASSERT_EQ(1U, lights.size());
     ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+    ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
 
     ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
     ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index f4ecba2..55c2db6 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -21,7 +21,6 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
-
 cc_fuzz {
     name: "inputflinger_latencytracker_fuzzer",
     defaults: [
@@ -34,7 +33,6 @@
         "libbase",
         "libbinder",
         "liblog",
-        "libui",
         "libutils",
         "libinput",
         "libinputflinger",
@@ -43,7 +41,7 @@
         "LatencyTrackerFuzzer.cpp",
     ],
     fuzz_config: {
-       cc: ["android-framework-input@google.com"],
+        cc: ["android-framework-input@google.com"],
     },
 }
 
@@ -63,7 +61,6 @@
         "libcutils",
         "liblog",
         "libutils",
-        "libui",
         "libinput",
         "libinputflinger",
         "libinputreader",
@@ -75,7 +72,7 @@
         "libinputreader_headers",
     ],
     fuzz_config: {
-       cc: ["android-framework-input@google.com"],
+        cc: ["android-framework-input@google.com"],
     },
 }
 
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index f5bd297..a9f5a3a 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -169,7 +169,6 @@
     std::shared_ptr<FuzzEventHub> fuzzEventHub = std::make_shared<FuzzEventHub>(fdp);
     std::unique_ptr<FuzzInputReader> reader =
             std::make_unique<FuzzInputReader>(fuzzEventHub, fuzzPolicy, fuzzListener);
-    fuzzEventHub->addEvents(fdp);
     size_t patternCount = fdp->ConsumeIntegralInRange<size_t>(1, 260);
     VibrationSequence pattern(patternCount);
     for (size_t i = 0; i < patternCount; ++i) {
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 53a7b16..03c2266 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -114,30 +114,12 @@
     InputDeviceIdentifier mIdentifier;
     std::vector<TouchVideoFrame> mVideoFrames;
     PropertyMap mFuzzConfig;
-    size_t mCount = 0;
-    std::array<RawEvent, kMaxSize> mBuf;
     std::shared_ptr<FuzzedDataProvider> mFdp;
 
 public:
     FuzzEventHub(std::shared_ptr<FuzzedDataProvider> fdp) : mFdp(std::move(fdp)) {}
     ~FuzzEventHub() {}
     void addProperty(std::string key, std::string value) { mFuzzConfig.addProperty(key, value); }
-    void addEvents(std::shared_ptr<FuzzedDataProvider> fdp) {
-        mCount = fdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
-
-        for (size_t i = 0; i < mCount; ++i) {
-            int32_t type = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidTypes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-            int32_t code = fdp->ConsumeBool() ? fdp->PickValueInArray(kValidCodes)
-                                              : fdp->ConsumeIntegral<int32_t>();
-            mBuf[i] = {fdp->ConsumeIntegral<nsecs_t>(),
-                       fdp->ConsumeIntegral<nsecs_t>(),
-                       fdp->ConsumeIntegral<int32_t>(),
-                       type,
-                       code,
-                       fdp->ConsumeIntegral<int32_t>()};
-        }
-    }
 
     ftl::Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
         return ftl::Flags<InputDeviceClass>(mFdp->ConsumeIntegral<uint32_t>());
@@ -168,10 +150,24 @@
         return mFdp->ConsumeIntegral<status_t>();
     }
     void setExcludedDevices(const std::vector<std::string>& devices) override {}
-    size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override {
-        for (size_t i = 0; i < mCount; ++i) buffer[i] = mBuf[i];
-
-        return mCount;
+    std::vector<RawEvent> getEvents(int timeoutMillis) override {
+        std::vector<RawEvent> events;
+        const size_t count = mFdp->ConsumeIntegralInRange<size_t>(0, kMaxSize);
+        for (size_t i = 0; i < count; ++i) {
+            int32_t type = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidTypes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            int32_t code = mFdp->ConsumeBool() ? mFdp->PickValueInArray(kValidCodes)
+                                               : mFdp->ConsumeIntegral<int32_t>();
+            events.push_back({
+                    .when = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .readTime = mFdp->ConsumeIntegral<nsecs_t>(),
+                    .deviceId = mFdp->ConsumeIntegral<int32_t>(),
+                    .type = type,
+                    .code = code,
+                    .value = mFdp->ConsumeIntegral<int32_t>(),
+            });
+        }
+        return events;
     }
     std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { return mVideoFrames; }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 3faa068..6832ae1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -18,9 +18,10 @@
 
 #include <TimeStats/TimeStats.h>
 #include <utils/Timers.h>
-
 #include <memory>
 
+#include "Feature.h"
+
 namespace android {
 
 class HWComposer;
@@ -72,6 +73,8 @@
     // TODO(b/121291683): These will become private/internal
     virtual void preComposition(CompositionRefreshArgs&) = 0;
 
+    virtual FeatureFlags getFeatureFlags() const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
new file mode 100644
index 0000000..ee8000a
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 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 <ftl/flags.h>
+#include <cstdint>
+
+namespace android::compositionengine {
+
+enum class Feature : int32_t {
+    kSnapshotLayerMetadata = 1 << 0,
+};
+
+using FeatureFlags = ftl::Flags<Feature>;
+
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a738da0..fe8cad5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -39,6 +39,10 @@
 
 class Fence;
 
+namespace gui {
+struct LayerMetadata;
+}
+
 namespace compositionengine {
 
 struct LayerFECompositionState;
@@ -144,6 +148,8 @@
     // Whether the layer should be rendered with rounded corners.
     virtual bool hasRoundedCorners() const = 0;
     virtual void setWasClientComposed(const sp<Fence>&) {}
+    virtual const gui::LayerMetadata* getMetadata() const = 0;
+    virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 0907926..dd4dbe9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -48,6 +48,8 @@
 
     void preComposition(CompositionRefreshArgs&) override;
 
+    FeatureFlags getFeatureFlags() const override;
+
     // Debugging
     void dump(std::string&) const override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index f953d0b..a48cc6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -53,6 +53,8 @@
 
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 
+    MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
+
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index be0dbce..14922a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -53,6 +53,8 @@
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
+    MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
+    MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 855507e..a4e1fff 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -140,6 +140,10 @@
     mNeedsAnotherUpdate = needsAnotherUpdate;
 }
 
+FeatureFlags CompositionEngine::getFeatureFlags() const {
+    return {};
+}
+
 void CompositionEngine::dump(std::string&) const {
     // The base class has no state to dump, but derived classes might.
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index d7704a8..9102139 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -70,6 +70,7 @@
     MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
     MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
+    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
     MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
     MOCK_METHOD3(setOutputBuffer,
                  status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index a25296c..f8115eb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -238,16 +238,6 @@
     return refreshRateConfigs().getActiveModePtr()->getVsyncPeriod();
 }
 
-nsecs_t DisplayDevice::getRefreshTimestamp() const {
-    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    const auto vsyncPeriodNanos = getVsyncPeriodFromHWC();
-    return now - ((now - mLastHwVsync) % vsyncPeriodNanos);
-}
-
-void DisplayDevice::onVsync(nsecs_t timestamp) {
-    mLastHwVsync = timestamp;
-}
-
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 0f52aff..510df81 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -233,9 +233,7 @@
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
 
-    void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
-    nsecs_t getRefreshTimestamp() const;
 
     status_t setRefreshRatePolicy(
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
@@ -273,8 +271,6 @@
     std::optional<float> mStagedBrightness;
     float mBrightness = -1.f;
 
-    std::atomic<nsecs_t> mLastHwVsync = 0;
-
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 486eaf8..96399e2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -70,7 +70,7 @@
 struct ComposerCallback {
     virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
     virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
-    virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                                     std::optional<hal::VsyncPeriodNanos>) = 0;
     virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 15d5041..0a4ad97 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -144,7 +144,7 @@
     return mUpdateDeviceProductInfoOnHotplugReconnect;
 }
 
-bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
+bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, nsecs_t timestamp) {
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
@@ -160,13 +160,13 @@
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == displayData.lastHwVsync) {
+        if (timestamp == displayData.lastPresentTimestamp) {
             ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
                   to_string(*displayId).c_str(), timestamp);
             return false;
         }
 
-        displayData.lastHwVsync = timestamp;
+        displayData.lastPresentTimestamp = timestamp;
     }
 
     const auto tag = "HW_VSYNC_" + to_string(*displayId);
@@ -485,6 +485,11 @@
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
+nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    return mDisplayData.at(displayId).lastPresentTimestamp;
+}
+
 sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 92a8f30..6c43d8b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,8 +160,9 @@
     // reset state when a display is disconnected
     virtual void disconnectDisplay(HalDisplayId) = 0;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
+    virtual nsecs_t getPresentTimestamp(PhysicalDisplayId) const = 0;
 
     // Get last release fence for the given layer
     virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
@@ -214,7 +215,7 @@
     // TODO(b/157555476): Remove when the framework has proper support for headless mode
     virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
 
-    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    virtual bool onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;
     virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
 
     virtual bool isConnected(PhysicalDisplayId) const = 0;
@@ -343,8 +344,9 @@
     // reset state when a display is disconnected
     void disconnectDisplay(HalDisplayId) override;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     sp<Fence> getPresentFence(HalDisplayId) const override;
+    nsecs_t getPresentTimestamp(PhysicalDisplayId) const override;
 
     // Get last release fence for the given layer
     sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
@@ -387,7 +389,7 @@
 
     bool updatesDeviceProductInfoOnHotplugReconnect() const override;
 
-    bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    bool onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
     void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
 
     bool isConnected(PhysicalDisplayId) const override;
@@ -456,7 +458,10 @@
 
     struct DisplayData {
         std::unique_ptr<HWC2::Display> hwcDisplay;
+
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
+        nsecs_t lastPresentTimestamp = 0;
+
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
 
         bool validateWasSkipped;
@@ -466,8 +471,6 @@
 
         std::mutex vsyncEnabledLock;
         hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
-
-        nsecs_t lastHwVsync = 0;
     };
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 13437a4..410e438 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3918,9 +3918,14 @@
             mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
         } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                    displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
-            // The HWC doesn't support present fences, so use the refresh
-            // timestamp instead.
-            const nsecs_t actualPresentTime = display->getRefreshTimestamp();
+            // The HWC doesn't support present fences, so use the present timestamp instead.
+            const nsecs_t presentTimestamp =
+                    mFlinger->getHwComposer().getPresentTimestamp(*displayId);
+
+            const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+            const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+            const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
+
             mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
                                                  refreshRate, renderRate, vote, gameMode);
             mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
@@ -4241,6 +4246,38 @@
     preparePerFrameCompositionState();
 }
 
+void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
+    mSnapshot->layerMetadata = parentMetadata;
+    mSnapshot->layerMetadata.merge(mDrawingState.metadata);
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->updateMetadataSnapshot(mSnapshot->layerMetadata);
+    }
+}
+
+void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                           std::unordered_set<Layer*>& visited) {
+    if (visited.find(this) != visited.end()) {
+        ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
+        return;
+    }
+    visited.insert(this);
+
+    mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
+
+    if (mDrawingState.zOrderRelatives.empty()) {
+        return;
+    }
+    LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
+    childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
+    for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> relative = weakRelative.promote();
+        if (!relative) {
+            continue;
+        }
+        relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4079d16..4ff86e5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -166,6 +166,8 @@
         ui::Transform transform;
         Rect bufferSize;
         std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+        LayerMetadata layerMetadata;
+        LayerMetadata relativeLayerMetadata;
     };
 
     using FrameRate = scheduler::LayerInfo::FrameRate;
@@ -611,6 +613,12 @@
         mClearClientCompositionFenceOnLayerDisplayed = false;
     }
 
+    const LayerMetadata* getMetadata() const override { return &mSnapshot->layerMetadata; }
+
+    const LayerMetadata* getRelativeMetadata() const override {
+        return &mSnapshot->relativeLayerMetadata;
+    }
+
     const char* getDebugName() const override;
 
     bool setShadowRadius(float shadowRadius);
@@ -901,6 +909,9 @@
     // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
     // CompositionEngine to create a single path for composing layers.
     void updateSnapshot(bool updateGeometry);
+    void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
+    void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                        std::unordered_set<Layer*>& visited);
 
 protected:
     friend class impl::SurfaceInterceptor;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e27c713..7d3fc98 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1893,17 +1893,12 @@
     ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
 
     Mutex::Autolock lock(mStateLock);
-    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
-    if (displayId) {
-        const auto token = getPhysicalDisplayTokenLocked(*displayId);
-        const auto display = getDisplayDeviceLocked(token);
-        display->onVsync(timestamp);
-    }
 
     if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
         return;
     }
 
+    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
     const bool isActiveDisplay =
             displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
     if (!isActiveDisplay) {
@@ -2211,6 +2206,13 @@
     }
     mPowerAdvisor->setDisplays(displayIds);
 
+    const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
+            compositionengine::Feature::kSnapshotLayerMetadata);
+    if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
+        updateLayerMetadataSnapshot();
+        mLayerMetadataSnapshotNeeded = false;
+    }
+
     if (DOES_CONTAIN_BORDER) {
         refreshArgs.borderInfoList.clear();
         mDrawingState.traverse([&refreshArgs](Layer* layer) {
@@ -4445,7 +4447,10 @@
             layer->setGameModeForTree(static_cast<GameMode>(gameMode));
         }
 
-        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+        if (layer->setMetadata(s.metadata)) {
+            flags |= eTraversalNeeded;
+            mLayerMetadataSnapshotNeeded = true;
+        }
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
         if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
@@ -7240,6 +7245,31 @@
     return true;
 }
 
+void SurfaceFlinger::updateLayerMetadataSnapshot() {
+    LayerMetadata parentMetadata;
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        layer->updateMetadataSnapshot(parentMetadata);
+    }
+
+    std::unordered_set<Layer*> visited;
+    mDrawingState.traverse([&visited](Layer* layer) {
+        if (visited.find(layer) != visited.end()) {
+            return;
+        }
+
+        // If the layer isRelativeOf, then either it's relative metadata will be set
+        // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
+        // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
+        // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
+        if (layer->getDrawingState().isRelativeOf) {
+            layer->editLayerSnapshot()->relativeLayerMetadata = {};
+            return;
+        }
+
+        layer->updateRelativeMetadataSnapshot({}, visited);
+    });
+}
+
 // gui::ISurfaceComposer
 
 binder::Status SurfaceComposerAIDL::bootFinished() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3c92d56..85d11ba 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -608,7 +608,7 @@
     void binderDied(const wp<IBinder>& who) override;
 
     // HWC2::ComposerCallback overrides:
-    void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
     void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
     void onComposerHalRefresh(hal::HWDisplayId) override;
@@ -699,6 +699,7 @@
     bool latchBuffers();
 
     void updateLayerGeometry();
+    void updateLayerMetadataSnapshot();
 
     void updateInputFlinger();
     void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -1176,6 +1177,9 @@
     bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
+    // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
+    bool mLayerMetadataSnapshotNeeded = false;
+
     // Tracks layers that have pending frames which are candidates for being
     // latched.
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index dec14d0..4469df0 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -109,6 +109,7 @@
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
+        "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
new file mode 100644
index 0000000..0cf3bdf
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -0,0 +1,212 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
+public:
+    SurfaceFlingerUpdateLayerMetadataSnapshotTest() { setupScheduler(); }
+
+protected:
+    void setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                std::move(eventThread), std::move(sfEventThread));
+    }
+
+    sp<Layer> createLayer(const char* name, LayerMetadata layerMetadata) {
+        return sp<Layer>::make(
+                LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, layerMetadata});
+    }
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+class LayerMetadataBuilder {
+public:
+    LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
+
+    LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
+        mLayerMetadata.setInt32(key, value);
+        return *this;
+    }
+
+    LayerMetadata build() { return mLayerMetadata; }
+
+private:
+    LayerMetadata mLayerMetadata;
+};
+
+bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
+    return lhs.mMap == rhs.mMap;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
+    stream << "LayerMetadata{";
+    for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
+        if (it != layerMetadata.mMap.cbegin()) {
+            stream << ", ";
+        }
+        stream << layerMetadata.itemToString(it->first, ":");
+    }
+    return stream << "}";
+}
+
+// Test that the snapshot's layer metadata is set.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
+    auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layer = createLayer("layer", layerMetadata);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+}
+
+// Test that snapshot layer metadata is set by merging the child's metadata on top of its
+// parent's metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_OWNER_UID, 1)
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .build();
+    auto layerA = createLayer("parent", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
+    auto layerB = createLayer("child", layerBMetadata);
+    layerA->addChild(layerB);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+    auto expectedChildMetadata =
+            LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
+    ASSERT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+}
+
+// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
+// that parent's relative layer metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layerA = createLayer("relative-parent", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
+    auto layerB = createLayer("relative-child", layerBMetadata);
+    layerB->setRelativeLayer(layerAHandle, 1);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
+// two other layers.
+//
+// Layer
+//      A
+//     / \
+//    B   D
+//   /
+//  C
+//
+// Z-order Relatives
+//    B <- D <- C
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .setInt32(METADATA_OWNER_PID, 3)
+                                  .build();
+    auto layerB = createLayer("layer-b", layerBMetadata);
+    auto layerBHandle = layerB->getHandle();
+    auto layerC = createLayer("layer-c", {});
+    auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
+    auto layerD = createLayer("layer-d", layerDMetadata);
+    auto layerDHandle = layerD->getHandle();
+    layerB->addChild(layerC);
+    layerA->addChild(layerB);
+    layerA->addChild(layerD);
+    layerC->setRelativeLayer(layerDHandle, 1);
+    layerD->setRelativeLayer(layerBHandle, 1);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    auto expectedLayerDRelativeMetadata = LayerMetadataBuilder()
+                                                  // From layer A, parent of relative parent
+                                                  .setInt32(METADATA_OWNER_UID, 1)
+                                                  // From layer B, relative parent
+                                                  .setInt32(METADATA_TASK_ID, 2)
+                                                  .setInt32(METADATA_OWNER_PID, 3)
+                                                  .build();
+    ASSERT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+    auto expectedLayerCRelativeMetadata =
+            LayerMetadataBuilder()
+                    // From layer A, parent of relative parent
+                    .setInt32(METADATA_OWNER_UID, 1)
+                    // From layer B, relative parent of relative parent
+                    .setInt32(METADATA_OWNER_PID, 3)
+                    // From layer D, relative parent
+                    .setInt32(METADATA_TASK_ID, 4)
+                    .build();
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+}
+
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
+       updatesRelativeMetadataMultipleRelativeChildren) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerB = createLayer("layer-b", {});
+    auto layerC = createLayer("layer-c", {});
+    layerB->setRelativeLayer(layerAHandle, 1);
+    layerC->setRelativeLayer(layerAHandle, 2);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    ASSERT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    ASSERT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+    ASSERT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2c9c451..13389a1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -477,6 +477,8 @@
         return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
     }
 
+    void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 5719b5c..a87f82f 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -27,6 +27,9 @@
     symbol_file: "libvulkan.map.txt",
     first_version: "24",
     unversioned_until: "current",
+    export_header_libs: [
+        "ndk_vulkan_headers",
+    ],
 }
 
 cc_library_shared {