Merge "Move key remapping to InputReader thread(1/n)" into main
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 358a191..c98b9cf 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -16,8 +16,12 @@
 
 #pragma once
 
+#include <map>
+#include <memory>
+#include <optional>
+
+#include <input/Input.h>
 #include <input/InputTransport.h>
-#include <input/LooperInterface.h>
 #include <input/Resampler.h>
 #include <utils/Looper.h>
 
@@ -36,7 +40,7 @@
     /**
      * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
      * If you don't want batching, then call "consumeBatchedInputEvents" immediately with
-     * std::nullopt frameTime to receive the pending motion event(s).
+     * std::nullopt requestedFrameTime to receive the pending motion event(s).
      * @param pendingBatchSource the source of the pending batch.
      */
     virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0;
@@ -67,16 +71,6 @@
 class InputConsumerNoResampling final {
 public:
     /**
-     * This constructor is exclusively for test code. Any real use of InputConsumerNoResampling must
-     * use the constructor that takes an sp<Looper> parameter instead of
-     * std::shared_ptr<LooperInterface>.
-     */
-    explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
-                                       std::shared_ptr<LooperInterface> looper,
-                                       InputConsumerCallbacks& callbacks,
-                                       std::unique_ptr<Resampler> resampler);
-
-    /**
      * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
      * the event is ready to consume.
      * @param looper needs to be sp and not shared_ptr because it inherits from
@@ -96,15 +90,17 @@
     void finishInputEvent(uint32_t seq, bool handled);
     void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
     /**
-     * If you want to consume all events immediately (disable batching), the you still must call
-     * this. For frameTime, use a std::nullopt.
-     * @param frameTime the time up to which consume the events. When there's double (or triple)
-     * buffering, you may want to not consume all events currently available, because you could be
-     * still working on an older frame, but there could already have been events that arrived that
-     * are more recent.
+     * If you want to consume all events immediately (disable batching), then you still must call
+     * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption
+     * will occur at requestedFrameTime. The resampling strategy may modify it.
+     * @param requestedFrameTime the time up to which consume the events. When there's double (or
+     * triple) buffering, you may want to not consume all events currently available, because you
+     * could be still working on an older frame, but there could already have been events that
+     * arrived that are more recent.
      * @return whether any events were actually consumed
      */
-    bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime);
+    bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime);
+
     /**
      * Returns true when there is *likely* a pending batch or a pending event in the channel.
      *
@@ -119,7 +115,7 @@
 
 private:
     std::shared_ptr<InputChannel> mChannel;
-    std::shared_ptr<LooperInterface> mLooper;
+    sp<Looper> mLooper;
     InputConsumerCallbacks& mCallbacks;
     std::unique_ptr<Resampler> mResampler;
 
@@ -200,20 +196,33 @@
     /**
      * Batched InputMessages, per deviceId.
      * For each device, we are storing a queue of batched messages. These will all be collapsed into
-     * a single MotionEvent (up to a specific frameTime) when the consumer calls
+     * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls
      * `consumeBatchedInputEvents`.
      */
     std::map<DeviceId, std::queue<InputMessage>> mBatches;
     /**
      * Creates a MotionEvent by consuming samples from the provided queue. If one message has
-     * eventTime > frameTime, all subsequent messages in the queue will be skipped. It is assumed
-     * that messages are queued in chronological order. In other words, only events that occurred
-     * prior to the requested frameTime will be consumed.
-     * @param frameTime the time up to which to consume events
+     * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is
+     * assumed that messages are queued in chronological order. In other words, only events that
+     * occurred prior to the adjustedFrameTime will be consumed.
+     * @param requestedFrameTime the time up to which to consume events.
      * @param messages the queue of messages to consume from
      */
     std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent(
-            const nsecs_t frameTime, std::queue<InputMessage>& messages);
+            const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages);
+
+    /**
+     * Consumes the batched input events, optionally allowing the caller to specify a device id
+     * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at
+     * requestedFrameTime.
+     * @param deviceId The device id from which to consume events. If std::nullopt, consumes events
+     * from any device id.
+     * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes
+     * input events with any timestamp.
+     * @return Whether or not any events were consumed.
+     */
+    bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId,
+                                   std::optional<nsecs_t> requestedFrameTime);
     /**
      * A map from a single sequence number to several sequence numbers. This is needed because of
      * batching. When batching is enabled, a single MotionEvent will contain several samples. Each
diff --git a/include/input/LooperInterface.h b/include/input/LooperInterface.h
deleted file mode 100644
index 2d6719c..0000000
--- a/include/input/LooperInterface.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Looper.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-/**
- * LooperInterface allows the use of TestLooper in InputConsumerNoResampling without reassigning to
- * Looper. LooperInterface is needed to control how InputConsumerNoResampling consumes and batches
- * InputMessages.
- */
-class LooperInterface {
-public:
-    virtual ~LooperInterface() = default;
-
-    virtual int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback,
-                      void* data) = 0;
-    virtual int removeFd(int fd) = 0;
-
-    virtual sp<Looper> getLooper() const = 0;
-};
-} // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 1d26d85..6698d0c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -232,6 +232,15 @@
     return cmd;
 }
 
+static void printReturnCommandParcel(std::ostream& out, const Parcel& parcel) {
+    const void* cmds = parcel.data();
+    out << "\t" << HexDump(cmds, parcel.dataSize()) << "\n";
+    IF_LOG_COMMANDS() {
+        const void* end = parcel.data() + parcel.dataSize();
+        while (cmds < end) cmds = printReturnCommand(out, cmds);
+    }
+}
+
 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;
@@ -1235,13 +1244,15 @@
 
     if (err >= NO_ERROR) {
         if (bwr.write_consumed > 0) {
-            if (bwr.write_consumed < mOut.dataSize())
+            if (bwr.write_consumed < mOut.dataSize()) {
+                std::ostringstream logStream;
+                printReturnCommandParcel(logStream, mIn);
                 LOG_ALWAYS_FATAL("Driver did not consume write buffer. "
-                                 "err: %s consumed: %zu of %zu",
-                                 statusToString(err).c_str(),
-                                 (size_t)bwr.write_consumed,
-                                 mOut.dataSize());
-            else {
+                                 "err: %s consumed: %zu of %zu.\n"
+                                 "Return command: %s",
+                                 statusToString(err).c_str(), (size_t)bwr.write_consumed,
+                                 mOut.dataSize(), logStream.str().c_str());
+            } else {
                 mOut.setDataSize(0);
                 processPostWriteDerefs();
             }
@@ -1252,14 +1263,8 @@
         }
         IF_LOG_COMMANDS() {
             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();
-            logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n";
-            while (cmds < end) cmds = printReturnCommand(logStream, cmds);
-            std::string message = logStream.str();
-            ALOGI("%s", message.c_str());
+            printReturnCommandParcel(logStream, mIn);
+            ALOGI("%s", logStream.str().c_str());
         }
         return NO_ERROR;
     }
diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index 5e0d4da..1d516ae 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -17,13 +17,6 @@
 #pragma once
 
 #include <android/binder_parcel.h>
-#if defined(__ANDROID_VENDOR__)
-#include <android/llndk-versioning.h>
-#else
-#if !defined(__INTRODUCED_IN_LLNDK)
-#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level)))
-#endif
-#endif  // __ANDROID_VENDOR__
 #include <stdbool.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
@@ -83,8 +76,7 @@
  *
  * \return Pointer to a new APersistableBundle
  */
-APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Create a new APersistableBundle based off an existing APersistableBundle.
@@ -98,7 +90,7 @@
  * \return Pointer to a new APersistableBundle
  */
 APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Delete an APersistableBundle. This must always be called when finished using
@@ -109,7 +101,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_delete(APersistableBundle* _Nullable pBundle)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Check for equality of APersistableBundles.
@@ -123,7 +115,7 @@
  */
 bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs,
                                 const APersistableBundle* _Nonnull rhs)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Read an APersistableBundle from an AParcel.
@@ -142,7 +134,7 @@
  */
 binder_status_t APersistableBundle_readFromParcel(
         const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Write an APersistableBundle to an AParcel.
@@ -162,7 +154,7 @@
  */
 binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle,
                                                  AParcel* _Nonnull parcel)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get the size of an APersistableBundle. This is the number of mappings in the
@@ -175,7 +167,7 @@
  * \return number of mappings in the object
  */
 int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Erase any entries added with the provided key.
@@ -188,7 +180,7 @@
  * \return number of entries erased. Either 0 or 1.
  */
 int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a boolean associated with the provided key.
@@ -201,8 +193,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                                   bool val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                   bool val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put an int32_t associated with the provided key.
@@ -215,8 +206,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                               int32_t val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                               int32_t val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put an int64_t associated with the provided key.
@@ -229,8 +219,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                                int64_t val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                int64_t val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a double associated with the provided key.
@@ -243,8 +232,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                                  double val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                  double val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a string associated with the provided key.
@@ -258,8 +246,7 @@
  * Available since API level 202404.
  */
 void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                                  const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                  const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a boolean vector associated with the provided key.
@@ -275,8 +262,7 @@
  */
 void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle,
                                          const char* _Nonnull key, const bool* _Nonnull vec,
-                                         int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                         int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put an int32_t vector associated with the provided key.
@@ -292,7 +278,7 @@
  */
 void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
                                      const int32_t* _Nonnull vec, int32_t num)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put an int64_t vector associated with the provided key.
@@ -308,8 +294,7 @@
  */
 void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle,
                                       const char* _Nonnull key, const int64_t* _Nonnull vec,
-                                      int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                      int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a double vector associated with the provided key.
@@ -325,8 +310,7 @@
  */
 void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle,
                                         const char* _Nonnull key, const double* _Nonnull vec,
-                                        int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                        int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put a string vector associated with the provided key.
@@ -343,7 +327,7 @@
 void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle,
                                         const char* _Nonnull key,
                                         const char* _Nullable const* _Nullable vec, int32_t num)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Put an APersistableBundle associated with the provided key.
@@ -359,7 +343,7 @@
 void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle,
                                              const char* _Nonnull key,
                                              const APersistableBundle* _Nonnull val)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a boolean associated with the provided key.
@@ -374,7 +358,7 @@
  */
 bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle,
                                    const char* _Nonnull key, bool* _Nonnull val)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get an int32_t associated with the provided key.
@@ -388,8 +372,7 @@
  * \return true if a value exists for the provided key
  */
 bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
-                               int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                               int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get an int64_t associated with the provided key.
@@ -404,7 +387,7 @@
  */
 bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle,
                                 const char* _Nonnull key, int64_t* _Nonnull val)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a double associated with the provided key.
@@ -419,7 +402,7 @@
  */
 bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle,
                                   const char* _Nonnull key, double* _Nonnull val)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a string associated with the provided key.
@@ -440,8 +423,7 @@
 int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
                                      const char* _Nonnull key, char* _Nullable* _Nonnull val,
                                      APersistableBundle_stringAllocator stringAllocator,
-                                     void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                     void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a boolean vector associated with the provided key and place it in the
@@ -468,7 +450,7 @@
 int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
                                             const char* _Nonnull key, bool* _Nullable buffer,
                                             int32_t bufferSizeBytes)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get an int32_t vector associated with the provided key and place it in the
@@ -494,8 +476,7 @@
  */
 int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
                                         const char* _Nonnull key, int32_t* _Nullable buffer,
-                                        int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                        int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get an int64_t vector associated with the provided key and place it in the
@@ -521,8 +502,8 @@
  */
 int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
                                          const char* _Nonnull key, int64_t* _Nullable buffer,
-                                         int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                         int32_t bufferSizeBytes)
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a double vector associated with the provided key and place it in the
@@ -549,7 +530,7 @@
 int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
                                            const char* _Nonnull key, double* _Nullable buffer,
                                            int32_t bufferSizeBytes)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get a string vector associated with the provided key and place it in the
@@ -586,7 +567,7 @@
                                            int32_t bufferSizeBytes,
                                            APersistableBundle_stringAllocator stringAllocator,
                                            void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get an APersistableBundle* associated with the provided key.
@@ -605,7 +586,7 @@
 bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle,
                                              const char* _Nonnull key,
                                              APersistableBundle* _Nullable* _Nonnull outBundle)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -638,7 +619,7 @@
                                           int32_t bufferSizeBytes,
                                           APersistableBundle_stringAllocator stringAllocator,
                                           void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -669,8 +650,7 @@
 int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
                                       char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
                                       APersistableBundle_stringAllocator stringAllocator,
-                                      void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                      void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -701,8 +681,7 @@
 int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
                                        char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
                                        APersistableBundle_stringAllocator stringAllocator,
-                                       void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                       void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -734,8 +713,8 @@
                                          char* _Nullable* _Nullable outKeys,
                                          int32_t bufferSizeBytes,
                                          APersistableBundle_stringAllocator stringAllocator,
-                                         void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                         void* _Nullable context)
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -767,8 +746,8 @@
                                          char* _Nullable* _Nullable outKeys,
                                          int32_t bufferSizeBytes,
                                          APersistableBundle_stringAllocator stringAllocator,
-                                         void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                         void* _Nullable context)
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -801,7 +780,7 @@
                                                 int32_t bufferSizeBytes,
                                                 APersistableBundle_stringAllocator stringAllocator,
                                                 void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -834,7 +813,7 @@
                                             int32_t bufferSizeBytes,
                                             APersistableBundle_stringAllocator stringAllocator,
                                             void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -867,7 +846,7 @@
                                              int32_t bufferSizeBytes,
                                              APersistableBundle_stringAllocator stringAllocator,
                                              void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -899,7 +878,7 @@
                                                int32_t bufferSizeBytes,
                                                APersistableBundle_stringAllocator stringAllocator,
                                                void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -932,7 +911,7 @@
                                                int32_t bufferSizeBytes,
                                                APersistableBundle_stringAllocator stringAllocator,
                                                void* _Nullable context)
-        __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Get all of the keys associated with this specific type and place it in the
@@ -963,6 +942,6 @@
 int32_t APersistableBundle_getPersistableBundleKeys(
         const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
         int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator,
-        void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+        void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
 
 __END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 41b30a0..cc4943b 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,7 +18,6 @@
 
 #include <android/binder_ibinder.h>
 #include <android/binder_status.h>
-#include <android/llndk-versioning.h>
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
@@ -257,8 +256,7 @@
  * \return the result of dlopen of the specified HAL
  */
 void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance,
-                                                 int flag) __INTRODUCED_IN(__ANDROID_API_V__)
-        __INTRODUCED_IN_LLNDK(202404);
+                                                 int flag) __INTRODUCED_IN(__ANDROID_API_V__);
 
 /**
  * Prevent lazy services without client from shutting down their process
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c65eafa..f5b3618 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -1135,6 +1135,17 @@
         AsyncWorker::getInstance().post(
                 [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); });
     }
+
+    void onBufferDetached(int slot) override {
+        AsyncWorker::getInstance().post(
+                [listener = mListener, slot = slot]() { listener->onBufferDetached(slot); });
+    }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+    void onBufferAttached() override {
+        AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferAttached(); });
+    }
+#endif
 };
 
 // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 7700795..8b9b090 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -184,4 +184,10 @@
 void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) {
 }
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+bool BnProducerListener::needsAttachNotify() {
+    return true;
+}
+#endif
+
 } // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index c5f9c38..f126c0b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -139,9 +139,9 @@
     uint32_t ignore;
     auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
                                  ISurfaceComposerClient::eOpaque);
-    mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
+    mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat,
                                        flags, mHandle, {}, &ignore);
-    mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);
+    mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat);
 
     // This surface is always consumed by SurfaceFlinger, so the
     // producerControlledByApp value doesn't matter; using false.
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 3dcc6b6..43bf6a7 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -90,6 +90,9 @@
             Parcel* reply, uint32_t flags = 0);
     virtual bool needsReleaseNotify();
     virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+    virtual bool needsAttachNotify();
+#endif
 };
 
 #else
@@ -103,6 +106,9 @@
     virtual ~StubProducerListener();
     virtual void onBufferReleased() {}
     virtual bool needsReleaseNotify() { return false; }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+    virtual bool needsAttachNotify() { return false; }
+#endif
 };
 
 } // namespace android
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index de55828..cdbc186 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -46,27 +46,6 @@
 const bool DEBUG_TRANSPORT_CONSUMER =
         __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
 
-/**
- * RealLooper is a wrapper of Looper. All the member functions exclusively call the internal looper.
- * This class' behavior is the same as Looper.
- */
-class RealLooper final : public LooperInterface {
-public:
-    RealLooper(sp<Looper> looper) : mLooper{looper} {}
-
-    int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback,
-              void* data) override {
-        return mLooper->addFd(fd, ident, events, callback, data);
-    }
-
-    int removeFd(int fd) override { return mLooper->removeFd(fd); }
-
-    sp<Looper> getLooper() const override { return mLooper; }
-
-private:
-    sp<Looper> mLooper;
-};
-
 std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) {
     std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
     event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
@@ -201,12 +180,12 @@
 // --- InputConsumerNoResampling ---
 
 InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
-                                                     std::shared_ptr<LooperInterface> looper,
+                                                     sp<Looper> looper,
                                                      InputConsumerCallbacks& callbacks,
                                                      std::unique_ptr<Resampler> resampler)
       : mChannel{channel},
         mLooper{looper},
-        mCallbacks(callbacks),
+        mCallbacks{callbacks},
         mResampler{std::move(resampler)},
         mFdEvents(0) {
     LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
@@ -218,16 +197,9 @@
     setFdEvents(ALOOPER_EVENT_INPUT);
 }
 
-InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
-                                                     sp<Looper> looper,
-                                                     InputConsumerCallbacks& callbacks,
-                                                     std::unique_ptr<Resampler> resampler)
-      : InputConsumerNoResampling(channel, std::make_shared<RealLooper>(looper), callbacks,
-                                  std::move(resampler)) {}
-
 InputConsumerNoResampling::~InputConsumerNoResampling() {
     ensureCalledOnLooperThread(__func__);
-    consumeBatchedInputEvents(std::nullopt);
+    consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
     while (!mOutboundQueue.empty()) {
         processOutboundEvents();
         // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
@@ -253,8 +225,7 @@
 
     int handledEvents = 0;
     if (events & ALOOPER_EVENT_INPUT) {
-        std::vector<InputMessage> messages = readAllMessages();
-        handleMessages(std::move(messages));
+        handleMessages(readAllMessages());
         handledEvents |= ALOOPER_EVENT_INPUT;
     }
 
@@ -362,10 +333,8 @@
                 // add it to batch
                 mBatches[deviceId].emplace(msg);
             } else {
-                // consume all pending batches for this event immediately
-                // TODO(b/329776327): figure out if this could be smarter by limiting the
-                // consumption only to the current device.
-                consumeBatchedInputEvents(std::nullopt);
+                // consume all pending batches for this device immediately
+                consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
                 handleMessage(msg);
             }
         } else {
@@ -483,13 +452,13 @@
 }
 
 std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
-InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime,
+InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
                                                     std::queue<InputMessage>& messages) {
     std::unique_ptr<MotionEvent> motionEvent;
     std::optional<uint32_t> firstSeqForBatch;
     const nanoseconds resampleLatency =
             (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0};
-    const nanoseconds adjustedFrameTime = nanoseconds{frameTime} - resampleLatency;
+    const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
 
     while (!messages.empty() &&
            (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
@@ -511,39 +480,55 @@
         if (!messages.empty()) {
             futureSample = &messages.front();
         }
-        mResampler->resampleMotionEvent(nanoseconds{frameTime}, *motionEvent, futureSample);
+        mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+                                        futureSample);
     }
     return std::make_pair(std::move(motionEvent), firstSeqForBatch);
 }
 
 bool InputConsumerNoResampling::consumeBatchedInputEvents(
-        std::optional<nsecs_t> requestedFrameTime) {
+        std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
     ensureCalledOnLooperThread(__func__);
     // When batching is not enabled, we want to consume all events. That's equivalent to having an
-    // infinite frameTime.
-    const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
+    // infinite requestedFrameTime.
+    requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
     bool producedEvents = false;
-    for (auto& [_, messages] : mBatches) {
-        auto [motion, firstSeqForBatch] = createBatchedMotionEvent(frameTime, messages);
+
+    for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
+                                                    : (mBatches.begin());
+         deviceIdIter != mBatches.cend(); ++deviceIdIter) {
+        std::queue<InputMessage>& messages = deviceIdIter->second;
+        auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
         if (motion != nullptr) {
             LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
             mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
             producedEvents = true;
         } else {
-            // This is OK, it just means that the frameTime is too old (all events that we have
-            // pending are in the future of the frametime). Maybe print a
-            // warning? If there are multiple devices active though, this might be normal and can
-            // just be ignored, unless none of them resulted in any consumption (in that case, this
-            // function would already return "false" so we could just leave it up to the caller).
+            // This is OK, it just means that the requestedFrameTime is too old (all events that we
+            // have pending are in the future of the requestedFrameTime). Maybe print a warning? If
+            // there are multiple devices active though, this might be normal and can just be
+            // ignored, unless none of them resulted in any consumption (in that case, this function
+            // would already return "false" so we could just leave it up to the caller).
+        }
+
+        if (deviceId.has_value()) {
+            // We already consumed events for this device. Break here to prevent iterating over the
+            // other devices.
+            break;
         }
     }
     std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); });
     return producedEvents;
 }
 
+bool InputConsumerNoResampling::consumeBatchedInputEvents(
+        std::optional<nsecs_t> requestedFrameTime) {
+    return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime);
+}
+
 void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const {
     sp<Looper> callingThreadLooper = Looper::getForThread();
-    if (callingThreadLooper != mLooper->getLooper()) {
+    if (callingThreadLooper != mLooper) {
         LOG(FATAL) << "The function " << func << " can only be called on the looper thread";
     }
 }
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 3ec167a..81c6175 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -27,7 +27,6 @@
         "Resampler_test.cpp",
         "RingBuffer_test.cpp",
         "TestInputChannel.cpp",
-        "TestLooper.cpp",
         "TfLiteMotionPredictor_test.cpp",
         "TouchResampling_test.cpp",
         "TouchVideoFrame_test.cpp",
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index 55be453..d708316 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -18,14 +18,15 @@
 
 #include <memory>
 #include <optional>
-#include <utility>
 
+#include <TestEventMatchers.h>
 #include <TestInputChannel.h>
-#include <TestLooper.h>
 #include <android-base/logging.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <input/BlockingQueue.h>
 #include <input/InputEventBuilders.h>
+#include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
 namespace android {
@@ -34,23 +35,44 @@
 
 using std::chrono::nanoseconds;
 
+using ::testing::AllOf;
+using ::testing::Matcher;
+using ::testing::Not;
+
 } // namespace
 
 class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
 protected:
     InputConsumerTest()
           : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
-            mTestLooper{std::make_shared<TestLooper>()} {
-        Looper::setForThread(mTestLooper->getLooper());
+            mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+        Looper::setForThread(mLooper);
         mConsumer =
-                std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mTestLooper, *this,
+                std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
                                                             std::make_unique<LegacyResampler>());
     }
 
-    void assertOnBatchedInputEventPendingWasCalled();
+    void invokeLooperCallback() const {
+        sp<LooperCallback> callback;
+        ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+                                             /*events=*/nullptr, &callback, /*data=*/nullptr));
+        callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+    }
+
+    void assertOnBatchedInputEventPendingWasCalled() {
+        ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+                << "onBatchedInputEventPending has not been called.";
+        --mOnBatchedInputEventPendingInvocationCount;
+    }
+
+    void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+        std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
+        ASSERT_NE(motionEvent, nullptr);
+        EXPECT_THAT(*motionEvent, matcher);
+    }
 
     std::shared_ptr<TestInputChannel> mClientTestChannel;
-    std::shared_ptr<TestLooper> mTestLooper;
+    sp<Looper> mLooper;
     std::unique_ptr<InputConsumerNoResampling> mConsumer;
 
     BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
@@ -96,12 +118,6 @@
     };
 };
 
-void InputConsumerTest::assertOnBatchedInputEventPendingWasCalled() {
-    ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
-            << "onBatchedInputEventPending has not been called.";
-    --mOnBatchedInputEventPendingInvocationCount;
-}
-
 TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
     mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
                                                .eventTime(nanoseconds{0ms}.count())
@@ -118,11 +134,11 @@
 
     mClientTestChannel->assertNoSentMessages();
 
-    mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT);
+    invokeLooperCallback();
 
     assertOnBatchedInputEventPendingWasCalled();
 
-    mConsumer->consumeBatchedInputEvents(std::nullopt);
+    mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
 
     std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
     ASSERT_NE(downMotionEvent, nullptr);
@@ -156,7 +172,7 @@
 
     mClientTestChannel->assertNoSentMessages();
 
-    mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT);
+    invokeLooperCallback();
 
     assertOnBatchedInputEventPendingWasCalled();
 
@@ -182,4 +198,50 @@
     mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
     mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
 }
+
+TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+                                               .deviceId(0)
+                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .build());
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+                                               .deviceId(0)
+                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .build());
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+                                               .deviceId(0)
+                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .build());
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+                                               .deviceId(0)
+                                               .action(AMOTION_EVENT_ACTION_MOVE)
+                                               .build());
+
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
+                                               .deviceId(1)
+                                               .action(AMOTION_EVENT_ACTION_DOWN)
+                                               .build());
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+    mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
+                                               .deviceId(0)
+                                               .action(AMOTION_EVENT_ACTION_UP)
+                                               .build());
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+                                    Not(MotionEventIsResampled())));
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 467c3b4..1210f71 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -364,7 +364,7 @@
         if (!mConsumer->probablyHasInput()) {
             ADD_FAILURE() << "should deterministically have input because there is a batch";
         }
-        mConsumer->consumeBatchedInputEvents(std::nullopt);
+        mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
     };
     void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
         mFocusEvents.push(std::move(event));
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
new file mode 100644
index 0000000..dd2e40c
--- /dev/null
+++ b/libs/input/tests/TestEventMatchers.h
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2024 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 <ostream>
+
+#include <input/Input.h>
+
+namespace android {
+
+/**
+ * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
+ * implementations must not be duplicated.
+ * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput.
+ */
+
+class WithDeviceIdMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {}
+
+    bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+        return mDeviceId == event.getDeviceId();
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+    const DeviceId mDeviceId;
+};
+
+inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+    return WithDeviceIdMatcher(deviceId);
+}
+
+class WithMotionActionMatcher {
+public:
+    using is_gtest_matcher = void;
+    explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+    bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+        bool matches = mAction == event.getAction();
+        if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+            matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+        }
+        return matches;
+    }
+
+    void DescribeTo(std::ostream* os) const {
+        *os << "with motion action " << MotionEvent::actionToString(mAction);
+        if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+            *os << " and FLAG_CANCELED";
+        }
+    }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+    const int32_t mAction;
+};
+
+inline WithMotionActionMatcher WithMotionAction(int32_t action) {
+    return WithMotionActionMatcher(action);
+}
+
+class MotionEventIsResampledMatcher {
+public:
+    using is_gtest_matcher = void;
+
+    bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
+        const size_t numSamples = motionEvent.getHistorySize() + 1;
+        const size_t numPointers = motionEvent.getPointerCount();
+        if (numPointers <= 0 || numSamples <= 0) {
+            return false;
+        }
+        for (size_t i = 0; i < numPointers; ++i) {
+            const PointerCoords& pointerCoords =
+                    motionEvent.getSamplePointerCoords()[numSamples * numPointers + i];
+            if (!pointerCoords.isResampled) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; }
+
+    void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; }
+};
+
+inline MotionEventIsResampledMatcher MotionEventIsResampled() {
+    return MotionEventIsResampledMatcher();
+}
+} // namespace android
diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp
index d5f00b6..26a0ca2 100644
--- a/libs/input/tests/TestInputChannel.cpp
+++ b/libs/input/tests/TestInputChannel.cpp
@@ -19,6 +19,11 @@
 
 #include <TestInputChannel.h>
 
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <array>
+
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/IBinder.h>
@@ -27,13 +32,25 @@
 namespace android {
 
 namespace {
-constexpr int FAKE_FD{-1};
+
+/**
+ * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned
+ * fd can be used to construct an InputChannel.
+ */
+base::unique_fd generateFileDescriptor() {
+    std::array<int, 2> kFileDescriptors;
+    LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0)
+            << "TestInputChannel. Failed to create socket pair.";
+    LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0)
+            << "TestInputChannel. Failed to close file descriptor.";
+    return base::unique_fd{kFileDescriptors[0]};
+}
 } // namespace
 
 // --- TestInputChannel ---
 
 TestInputChannel::TestInputChannel(const std::string& name)
-      : InputChannel{name, base::unique_fd(FAKE_FD), sp<BBinder>::make()} {}
+      : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {}
 
 void TestInputChannel::enqueueMessage(const InputMessage& message) {
     mReceivedMessages.push(message);
diff --git a/libs/input/tests/TestLooper.cpp b/libs/input/tests/TestLooper.cpp
deleted file mode 100644
index e0f01ed..0000000
--- a/libs/input/tests/TestLooper.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <TestLooper.h>
-
-#include <android-base/logging.h>
-
-namespace android {
-
-TestLooper::TestLooper() : mLooper(sp<Looper>::make(/*allowNonCallbacks=*/false)) {}
-
-int TestLooper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback,
-                      void* data) {
-    mCallbacks[fd] = callback;
-    constexpr int SUCCESS{1};
-    return SUCCESS;
-}
-
-int TestLooper::removeFd(int fd) {
-    if (auto it = mCallbacks.find(fd); it != mCallbacks.cend()) {
-        mCallbacks.erase(fd);
-        constexpr int SUCCESS{1};
-        return SUCCESS;
-    }
-    constexpr int FAILURE{0};
-    return FAILURE;
-}
-
-void TestLooper::invokeCallback(int fd, int events) {
-    auto it = mCallbacks.find(fd);
-    LOG_IF(FATAL, it == mCallbacks.cend()) << "Fd does not exist in mCallbacks.";
-    mCallbacks[fd]->handleEvent(fd, events, /*data=*/nullptr);
-}
-
-sp<Looper> TestLooper::getLooper() const {
-    return mLooper;
-}
-} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/TestLooper.h b/libs/input/tests/TestLooper.h
deleted file mode 100644
index 3242bc7..0000000
--- a/libs/input/tests/TestLooper.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright 2024 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 <map>
-
-#include <input/LooperInterface.h>
-
-namespace android {
-/**
- * TestLooper provides a mechanism to directly trigger Looper's callback.
- */
-class TestLooper final : public LooperInterface {
-public:
-    TestLooper();
-
-    /**
-     * Adds a file descriptor to mCallbacks. Ident, events, and data parameters are ignored. If
-     * addFd is called with an existent file descriptor and a different callback, the previous
-     * callback is overwritten.
-     */
-    int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback,
-              void* data) override;
-
-    /**
-     * Removes a file descriptor from mCallbacks. If fd is not in mCallbacks, returns FAILURE.
-     */
-    int removeFd(int fd) override;
-
-    /**
-     * Calls handleEvent of the file descriptor. Fd must be in mCallbacks. Otherwise, invokeCallback
-     * fatally logs.
-     */
-    void invokeCallback(int fd, int events);
-
-    sp<Looper> getLooper() const override;
-
-private:
-    std::map<int /*fd*/, sp<LooperCallback>> mCallbacks;
-    sp<Looper> mLooper;
-};
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ac15b92..ee605b7 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -279,24 +279,6 @@
           snapshot.getDebugString().c_str());
 }
 
-bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
-    if (requested.potentialCursor) {
-        return false;
-    }
-
-    if (snapshot.inputInfo.token != nullptr) {
-        return true;
-    }
-
-    if (snapshot.hasBufferOrSidebandStream()) {
-        return true;
-    }
-
-    return requested.windowInfoHandle &&
-            requested.windowInfoHandle->getInfo()->inputConfig.test(
-                    gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-}
-
 void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
                     const LayerSnapshotBuilder::Args& args) {
     snapshot.metadata.clear();
@@ -1162,7 +1144,7 @@
     }
 
     updateVisibility(snapshot, snapshot.isVisible);
-    if (!needsInputInfo(snapshot, requested)) {
+    if (!requested.needsInputInfo()) {
         return;
     }
 
@@ -1172,7 +1154,7 @@
     bool noValidDisplay = !displayInfoOpt.has_value();
     auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
 
-    if (!requested.windowInfoHandle) {
+    if (!requested.hasInputInfo()) {
         snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
     }
     fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 17d2610..5734ccf 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -62,6 +62,8 @@
     metadata.merge(args.metadata);
     changes |= RequestedLayerState::Changes::Metadata;
     handleAlive = true;
+    // TODO: b/305254099 remove once we don't pass invisible windows to input
+    windowInfoHandle = nullptr;
     if (parentId != UNASSIGNED_LAYER_ID) {
         canBeRoot = false;
     }
@@ -553,6 +555,24 @@
             windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
+bool RequestedLayerState::needsInputInfo() const {
+    if (potentialCursor) {
+        return false;
+    }
+
+    if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+        return true;
+    }
+
+    if (!windowInfoHandle) {
+        return false;
+    }
+
+    const auto windowInfo = windowInfoHandle->getInfo();
+    return windowInfo->token != nullptr ||
+            windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
 bool RequestedLayerState::hasBlur() const {
     return backgroundBlurRadius > 0 || blurRegions.size() > 0;
 }
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 48b9640..1d96dff 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -87,6 +87,7 @@
     aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
     bool hasValidRelativeParent() const;
     bool hasInputInfo() const;
+    bool needsInputInfo() const;
     bool hasBlur() const;
     bool hasFrameUpdate() const;
     bool hasReadyFrame() const;
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index b4efe0f..c7cc21c 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -619,4 +619,14 @@
     }
 }
 
+TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) {
+    // By default the layer has no buffer, so it doesn't need an input info
+    EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+
+    setBuffer(111);
+    mLifecycleManager.commitChanges();
+
+    EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 9020723..75d2fa3 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1762,6 +1762,7 @@
     UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
     EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
 }
+
 TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
     if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
         GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
@@ -1920,4 +1921,18 @@
     EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0);
 }
 
+TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) {
+    // By default the layer has no buffer, so we don't expect it to have an input info
+    EXPECT_FALSE(getSnapshot(111)->hasInputInfo());
+
+    setBuffer(111);
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_TRUE(getSnapshot(111)->hasInputInfo());
+    EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+            gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL));
+    EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index d92f891..9de3346 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -131,7 +131,9 @@
     using Scheduler::resyncAllToHardwareVsync;
 
     auto& mutableLayerHistory() { return mLayerHistory; }
-    auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
+    auto& mutableAttachedChoreographers() NO_THREAD_SAFETY_ANALYSIS {
+        return mAttachedChoreographers;
+    }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 347a396..9dae06a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -659,8 +659,10 @@
      * post-conditions.
      */
 
-    const auto& displays() const { return mFlinger->mDisplays; }
-    const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; }
+    const auto& displays() const NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; }
+    const auto& physicalDisplays() const NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->mPhysicalDisplays;
+    }
     const auto& currentState() const { return mFlinger->mCurrentState; }
     const auto& drawingState() const { return mFlinger->mDrawingState; }
     const auto& transactionFlags() const { return mFlinger->mTransactionFlags; }
@@ -673,13 +675,17 @@
     auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; }
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
-    auto& mutableDisplays() { return mFlinger->mDisplays; }
-    auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
+    auto& mutableDisplays() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; }
+    auto& mutablePhysicalDisplays() NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->mPhysicalDisplays;
+    }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
     auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
-    auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
+    auto& mutablePendingHotplugEvents() NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->mPendingHotplugEvents;
+    }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
     auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
@@ -687,7 +693,7 @@
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
-    auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
+    auto& mutableActiveDisplayId() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mActiveDisplayId; }
     auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; }
 
     auto& mutableActiveDisplayRotationFlags() {