Merge "[hwc-batching] Add option to override feature flag locally" into main
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
new file mode 100644
index 0000000..cd2c5df
--- /dev/null
+++ b/include/android/surface_control_input_receiver.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 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 <stdint.h>
+#include <android/input.h>
+#include <android/surface_control.h>
+#include <android/input_transfer_token_jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * The AInputReceiver_onMotionEvent callback is invoked when the registered input channel receives
+ * a motion event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param motionEvent The motion event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
+                                             AInputEvent *_Nonnull motionEvent)
+                                            __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives
+ * a key event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param keyEvent The key event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context,
+                                          AInputEvent *_Nonnull keyEvent)
+                                          __INTRODUCED_IN(__ANDROID_API_V__);
+
+struct AInputReceiverCallbacks;
+
+struct AInputReceiver;
+
+/**
+ * The InputReceiver that holds the reference to the registered input channel. This must be released
+ * using AInputReceiver_release
+ *
+ * Available since API level 35.
+ */
+typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive batched input event. For
+ * those events that are batched, the invocation will happen once per AChoreographer frame, and
+ * other input events will be delivered immediately.
+ *
+ * This is different from AInputReceiver_createUnbatchedInputReceiver in that the input events are
+ * received batched. The caller must invoke AInputReceiver_release to cleanv up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aChoreographer         The AChoreographer used for batching. This should match the
+ *                               rendering AChoreographer.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ *                               transferring touch gesture from host to embedded and for ANRs
+ *                               to ensure the host receives the ANR if any issues with
+ *                               touch on the embedded. This can be retrieved for the host window
+ *                               by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl        The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createBatchedInputReceiver(AChoreographer* _Nonnull aChoreographer,
+                                        const AInputTransferToken* _Nonnull hostInputTransferToken,
+                                        const ASurfaceControl* _Nonnull aSurfaceControl,
+                                        AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+                                        __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive every input event.
+ * This is different from AInputReceiver_createBatchedInputReceiver in that the input events are
+ * received unbatched. The caller must invoke AInputReceiver_release to clean up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aLooper                The looper to use when invoking callbacks.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ *                               transferring touch gesture from host to embedded and for ANRs
+ *                               to ensure the host receives the ANR if any issues with
+ *                               touch on the embedded. This can be retrieved for the host window
+ *                               by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl        The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createUnbatchedInputReceiver(ALooper* _Nonnull aLooper,
+                                         const AInputTransferToken* _Nonnull hostInputTransferToken,
+                                         const ASurfaceControl* _Nonnull aSurfaceControl,
+                                         AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+                                         __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Returns the AInputTransferToken that can be used to transfer touch gesture to or from other
+ * windows. This InputTransferToken is associated with the SurfaceControl that registered an input
+ * receiver and can be used with the host token for things like transfer touch gesture via
+ * WindowManager#transferTouchGesture().
+ *
+ * This must be released with AInputTransferToken_release.
+ *
+ * \param aInputReceiver The inputReceiver object to retrieve the AInputTransferToken for.
+ *
+ * Available since API level 35.
+ */
+const AInputTransferToken *_Nonnull
+AInputReceiver_getInputTransferToken(AInputReceiver *_Nonnull aInputReceiver)
+        __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Unregisters the input channel and deletes the AInputReceiver. This must be called on the same
+ * looper thread it was created with.
+ *
+ * \param aInputReceiver The inputReceiver object to release.
+ *
+ * Available since API level 35.
+ */
+void
+AInputReceiver_release(AInputReceiver *_Nonnull aInputReceiver) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Creates a AInputReceiverCallbacks object that is used when registering for an AInputReceiver.
+ * This must be released using AInputReceiverCallbacks_release
+ *
+ * \param context Optional context provided by the client that will be passed into the callbacks.
+ *
+ * Available since API level 35.
+ */
+AInputReceiverCallbacks* _Nonnull AInputReceiverCallbacks_create(void* _Nullable context)
+                                                        __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Releases the AInputReceiverCallbacks. This must be called on the same
+ * looper thread the AInputReceiver was created with. The receiver will not invoke any callbacks
+ * once it's been released.
+ *
+ * Available since API level 35
+ */
+void AInputReceiverCallbacks_release(AInputReceiverCallbacks* _Nonnull callbacks)
+                                     __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onMotionEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The motion event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+                                                AInputReceiver_onMotionEvent _Nonnull onMotionEvent)
+                                                __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onKeyEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The key event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+                                                 AInputReceiver_onKeyEvent _Nonnull onKeyEvent)
+                                                 __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index 7e6ae9b..9e48b08 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -29,8 +29,8 @@
 class InputConsumerCallbacks {
 public:
     virtual ~InputConsumerCallbacks(){};
-    virtual void onKeyEvent(KeyEvent&& event, uint32_t seq) = 0;
-    virtual void onMotionEvent(MotionEvent&& event, uint32_t seq) = 0;
+    virtual void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) = 0;
+    virtual void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) = 0;
     /**
      * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
      * If you don't want batching, then call "consumeBatchedInputEvents" immediately with
@@ -38,10 +38,10 @@
      * @param pendingBatchSource the source of the pending batch.
      */
     virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0;
-    virtual void onFocusEvent(FocusEvent&& event, uint32_t seq) = 0;
-    virtual void onCaptureEvent(CaptureEvent&& event, uint32_t seq) = 0;
-    virtual void onDragEvent(DragEvent&& event, uint32_t seq) = 0;
-    virtual void onTouchModeEvent(TouchModeEvent&& event, uint32_t seq) = 0;
+    virtual void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) = 0;
+    virtual void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) = 0;
+    virtual void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) = 0;
+    virtual void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) = 0;
 };
 
 /**
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 54290cd..0c8f3fa 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -425,4 +425,8 @@
     return iter->second;
 }
 
+const sp<Looper> Choreographer::getLooper() {
+    return mLooper;
+}
+
 } // namespace android
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index fc79b03..2e5aa4a 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -109,6 +109,7 @@
     virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
     int64_t getFrameInterval() const;
     bool inCallback() const;
+    const sp<Looper> getLooper();
 
 private:
     Choreographer(const Choreographer&) = delete;
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 1462c90..52acb51 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -44,28 +44,37 @@
 const bool DEBUG_TRANSPORT_CONSUMER =
         __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
 
-void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) {
-    event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
-                     msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action,
-                     msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode,
-                     msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime,
-                     msg.body.key.eventTime);
+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,
+                      msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action,
+                      msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode,
+                      msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime,
+                      msg.body.key.eventTime);
+    return event;
 }
 
-void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) {
-    event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
+std::unique_ptr<FocusEvent> createFocusEvent(const InputMessage& msg) {
+    std::unique_ptr<FocusEvent> event = std::make_unique<FocusEvent>();
+    event->initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
+    return event;
 }
 
-void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) {
-    event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
+std::unique_ptr<CaptureEvent> createCaptureEvent(const InputMessage& msg) {
+    std::unique_ptr<CaptureEvent> event = std::make_unique<CaptureEvent>();
+    event->initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
+    return event;
 }
 
-void initializeDragEvent(DragEvent& event, const InputMessage& msg) {
-    event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
-                     msg.body.drag.isExiting);
+std::unique_ptr<DragEvent> createDragEvent(const InputMessage& msg) {
+    std::unique_ptr<DragEvent> event = std::make_unique<DragEvent>();
+    event->initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
+                      msg.body.drag.isExiting);
+    return event;
 }
 
-void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) {
+std::unique_ptr<MotionEvent> createMotionEvent(const InputMessage& msg) {
+    std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
     const uint32_t pointerCount = msg.body.motion.pointerCount;
     std::vector<PointerProperties> pointerProperties;
     pointerProperties.reserve(pointerCount);
@@ -83,15 +92,16 @@
     displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw,
                           msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw,
                           0, 0, 1});
-    event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
-                     msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action,
-                     msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags,
-                     msg.body.motion.metaState, msg.body.motion.buttonState,
-                     msg.body.motion.classification, transform, msg.body.motion.xPrecision,
-                     msg.body.motion.yPrecision, msg.body.motion.xCursorPosition,
-                     msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime,
-                     msg.body.motion.eventTime, pointerCount, pointerProperties.data(),
-                     pointerCoords.data());
+    event->initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
+                      msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action,
+                      msg.body.motion.actionButton, msg.body.motion.flags,
+                      msg.body.motion.edgeFlags, msg.body.motion.metaState,
+                      msg.body.motion.buttonState, msg.body.motion.classification, transform,
+                      msg.body.motion.xPrecision, msg.body.motion.yPrecision,
+                      msg.body.motion.xCursorPosition, msg.body.motion.yCursorPosition,
+                      displayTransform, msg.body.motion.downTime, msg.body.motion.eventTime,
+                      pointerCount, pointerProperties.data(), pointerCoords.data());
+    return event;
 }
 
 void addSample(MotionEvent& event, const InputMessage& msg) {
@@ -107,8 +117,10 @@
     event.addSample(msg.body.motion.eventTime, pointerCoords.data());
 }
 
-void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) {
-    event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
+std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) {
+    std::unique_ptr<TouchModeEvent> event = std::make_unique<TouchModeEvent>();
+    event->initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
+    return event;
 }
 
 std::string outboundMessageToString(const InputMessage& outboundMsg) {
@@ -388,15 +400,13 @@
 void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const {
     switch (msg.header.type) {
         case InputMessage::Type::KEY: {
-            KeyEvent keyEvent;
-            initializeKeyEvent(keyEvent, msg);
+            std::unique_ptr<KeyEvent> keyEvent = createKeyEvent(msg);
             mCallbacks.onKeyEvent(std::move(keyEvent), msg.header.seq);
             break;
         }
 
         case InputMessage::Type::MOTION: {
-            MotionEvent motionEvent;
-            initializeMotionEvent(motionEvent, msg);
+            std::unique_ptr<MotionEvent> motionEvent = createMotionEvent(msg);
             mCallbacks.onMotionEvent(std::move(motionEvent), msg.header.seq);
             break;
         }
@@ -411,29 +421,25 @@
         }
 
         case InputMessage::Type::FOCUS: {
-            FocusEvent focusEvent;
-            initializeFocusEvent(focusEvent, msg);
+            std::unique_ptr<FocusEvent> focusEvent = createFocusEvent(msg);
             mCallbacks.onFocusEvent(std::move(focusEvent), msg.header.seq);
             break;
         }
 
         case InputMessage::Type::CAPTURE: {
-            CaptureEvent captureEvent;
-            initializeCaptureEvent(captureEvent, msg);
+            std::unique_ptr<CaptureEvent> captureEvent = createCaptureEvent(msg);
             mCallbacks.onCaptureEvent(std::move(captureEvent), msg.header.seq);
             break;
         }
 
         case InputMessage::Type::DRAG: {
-            DragEvent dragEvent;
-            initializeDragEvent(dragEvent, msg);
+            std::unique_ptr<DragEvent> dragEvent = createDragEvent(msg);
             mCallbacks.onDragEvent(std::move(dragEvent), msg.header.seq);
             break;
         }
 
         case InputMessage::Type::TOUCH_MODE: {
-            TouchModeEvent touchModeEvent;
-            initializeTouchModeEvent(touchModeEvent, msg);
+            std::unique_ptr<TouchModeEvent> touchModeEvent = createTouchModeEvent(msg);
             mCallbacks.onTouchModeEvent(std::move(touchModeEvent), msg.header.seq);
             break;
         }
@@ -448,7 +454,7 @@
     const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
     bool producedEvents = false;
     for (auto& [deviceId, messages] : mBatches) {
-        MotionEvent motion;
+        std::unique_ptr<MotionEvent> motion;
         std::optional<uint32_t> firstSeqForBatch;
         std::vector<uint32_t> sequences;
         while (!messages.empty()) {
@@ -456,20 +462,21 @@
             if (msg.body.motion.eventTime > frameTime) {
                 break;
             }
-            if (!firstSeqForBatch.has_value()) {
-                initializeMotionEvent(motion, msg);
+            if (motion == nullptr) {
+                motion = createMotionEvent(msg);
                 firstSeqForBatch = msg.header.seq;
                 const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}});
                 if (!inserted) {
                     LOG(FATAL) << "The sequence " << msg.header.seq << " was already present!";
                 }
             } else {
-                addSample(motion, msg);
+                addSample(*motion, msg);
                 mBatchedSequenceNumbers[*firstSeqForBatch].push_back(msg.header.seq);
             }
             messages.pop();
         }
-        if (firstSeqForBatch.has_value()) {
+        if (motion != nullptr) {
+            LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
             mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
             producedEvents = true;
         } else {
@@ -520,9 +527,8 @@
             std::queue<InputMessage> tmpQueue = messages;
             while (!tmpQueue.empty()) {
                 LOG_ALWAYS_FATAL_IF(tmpQueue.front().header.type != InputMessage::Type::MOTION);
-                MotionEvent motion;
-                initializeMotionEvent(motion, tmpQueue.front());
-                out += std::string("    ") + streamableToString(motion) + "\n";
+                std::unique_ptr<MotionEvent> motion = createMotionEvent(tmpQueue.front());
+                out += std::string("    ") + streamableToString(*motion) + "\n";
                 tmpQueue.pop();
             }
         }
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index e24ae49..6593497 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -341,19 +341,19 @@
     // The sequence number to use when publishing the next event
     uint32_t mSeq = 1;
 
-    BlockingQueue<KeyEvent> mKeyEvents;
-    BlockingQueue<MotionEvent> mMotionEvents;
-    BlockingQueue<FocusEvent> mFocusEvents;
-    BlockingQueue<CaptureEvent> mCaptureEvents;
-    BlockingQueue<DragEvent> mDragEvents;
-    BlockingQueue<TouchModeEvent> mTouchModeEvents;
+    BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
+    BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
+    BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
+    BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+    BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
+    BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
 
     // InputConsumerCallbacks interface
-    void onKeyEvent(KeyEvent&& event, uint32_t seq) override {
+    void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
         mKeyEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     }
-    void onMotionEvent(MotionEvent&& event, uint32_t seq) override {
+    void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
         mMotionEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     }
@@ -363,19 +363,19 @@
         }
         mConsumer->consumeBatchedInputEvents(std::nullopt);
     };
-    void onFocusEvent(FocusEvent&& event, uint32_t seq) override {
+    void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
         mFocusEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     };
-    void onCaptureEvent(CaptureEvent&& event, uint32_t seq) override {
+    void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
         mCaptureEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     };
-    void onDragEvent(DragEvent&& event, uint32_t seq) override {
+    void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
         mDragEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     }
-    void onTouchModeEvent(TouchModeEvent&& event, uint32_t seq) override {
+    void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
         mTouchModeEvents.push(std::move(event));
         mConsumer->finishInputEvent(seq, true);
     };
@@ -465,15 +465,15 @@
                                          eventTime);
     ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
 
-    std::optional<KeyEvent> keyEvent = mKeyEvents.popWithTimeout(TIMEOUT);
+    std::optional<std::unique_ptr<KeyEvent>> optKeyEvent = mKeyEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optKeyEvent.has_value()) << "consumer should have returned non-NULL event";
+    std::unique_ptr<KeyEvent> keyEvent = std::move(*optKeyEvent);
 
     sendMessage(LooperMessage::CALL_PROBABLY_HAS_INPUT);
     std::optional<bool> probablyHasInput = mProbablyHasInputResponses.popWithTimeout(TIMEOUT);
     ASSERT_TRUE(probablyHasInput.has_value());
     ASSERT_FALSE(probablyHasInput.value()) << "no events should be waiting after being consumed";
 
-    ASSERT_TRUE(keyEvent.has_value()) << "consumer should have returned non-NULL event";
-
     EXPECT_EQ(eventId, keyEvent->getId());
     EXPECT_EQ(deviceId, keyEvent->getDeviceId());
     EXPECT_EQ(source, keyEvent->getSource());
@@ -540,7 +540,8 @@
     publishMotionEvent(*mPublisher, args);
 
     // Ensure no event arrives because the UI thread is blocked
-    std::optional<MotionEvent> noEvent = mMotionEvents.popWithTimeout(NO_EVENT_TIMEOUT);
+    std::optional<std::unique_ptr<MotionEvent>> noEvent =
+            mMotionEvents.popWithTimeout(NO_EVENT_TIMEOUT);
     ASSERT_FALSE(noEvent.has_value()) << "Got unexpected event: " << *noEvent;
 
     Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
@@ -559,8 +560,9 @@
     }
     mNotifyLooperMayProceed.notify_all();
 
-    std::optional<MotionEvent> motion = mMotionEvents.popWithTimeout(TIMEOUT);
-    ASSERT_TRUE(motion.has_value());
+    std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optMotion.has_value());
+    std::unique_ptr<MotionEvent> motion = std::move(*optMotion);
     ASSERT_EQ(ACTION_MOVE, motion->getAction());
 
     verifyFinishedSignal(*mPublisher, seq, publishTime);
@@ -573,8 +575,9 @@
     nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
     publishMotionEvent(*mPublisher, args);
 
-    std::optional<MotionEvent> event = mMotionEvents.popWithTimeout(TIMEOUT);
-    ASSERT_TRUE(event.has_value()) << "consumer should have returned non-NULL event";
+    std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optMotion.has_value());
+    std::unique_ptr<MotionEvent> event = std::move(*optMotion);
 
     verifyArgsEqualToEvent(args, *event);
 
@@ -592,8 +595,9 @@
     status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
     ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
 
-    std::optional<FocusEvent> focusEvent = mFocusEvents.popWithTimeout(TIMEOUT);
-    ASSERT_TRUE(focusEvent.has_value()) << "consumer should have returned non-NULL event";
+    std::optional<std::unique_ptr<FocusEvent>> optFocusEvent = mFocusEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optFocusEvent.has_value()) << "consumer should have returned non-NULL event";
+    std::unique_ptr<FocusEvent> focusEvent = std::move(*optFocusEvent);
     EXPECT_EQ(eventId, focusEvent->getId());
     EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
 
@@ -611,9 +615,9 @@
     status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
     ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK";
 
-    std::optional<CaptureEvent> event = mCaptureEvents.popWithTimeout(TIMEOUT);
-
-    ASSERT_TRUE(event.has_value()) << "consumer should have returned non-NULL event";
+    std::optional<std::unique_ptr<CaptureEvent>> optEvent = mCaptureEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event";
+    std::unique_ptr<CaptureEvent> event = std::move(*optEvent);
 
     const CaptureEvent& captureEvent = *event;
     EXPECT_EQ(eventId, captureEvent.getId());
@@ -635,9 +639,9 @@
     status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting);
     ASSERT_EQ(OK, status) << "publisher publishDragEvent should return OK";
 
-    std::optional<DragEvent> event = mDragEvents.popWithTimeout(TIMEOUT);
-
-    ASSERT_TRUE(event.has_value()) << "consumer should have returned non-NULL event";
+    std::optional<std::unique_ptr<DragEvent>> optEvent = mDragEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event";
+    std::unique_ptr<DragEvent> event = std::move(*optEvent);
 
     const DragEvent& dragEvent = *event;
     EXPECT_EQ(eventId, dragEvent.getId());
@@ -659,8 +663,10 @@
     status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
     ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
 
-    std::optional<TouchModeEvent> event = mTouchModeEvents.popWithTimeout(TIMEOUT);
-    ASSERT_NE(std::nullopt, event);
+    std::optional<std::unique_ptr<TouchModeEvent>> optEvent =
+            mTouchModeEvents.popWithTimeout(TIMEOUT);
+    ASSERT_TRUE(optEvent.has_value());
+    std::unique_ptr<TouchModeEvent> event = std::move(*optEvent);
 
     const TouchModeEvent& touchModeEvent = *event;
     EXPECT_EQ(eventId, touchModeEvent.getId());
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index db1930d..acb3760 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -377,15 +377,20 @@
     mRenderRateOpt = renderRate;
     const auto renderPeriodDelta =
             prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
-    const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
-            mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
     if (applyImmediately) {
+        ATRACE_FORMAT_INSTANT("applyImmediately");
         while (mTimelines.size() > 1) {
             mTimelines.pop_front();
         }
 
         mTimelines.front().setRenderRate(renderRate);
-    } else if (newRenderRateIsHigher) {
+        return;
+    }
+
+    const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
+            mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
+    if (newRenderRateIsHigher) {
+        ATRACE_FORMAT_INSTANT("newRenderRateIsHigher");
         mTimelines.clear();
         mLastCommittedVsync = TimePoint::fromNs(0);
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 48707cb..aac1cac 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -944,6 +944,27 @@
     EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000));
 }
 
+// b/329310308
+TEST_F(VSyncPredictorTest, renderRateChangeAfterAppliedImmediately) {
+    tracker.addVsyncTimestamp(1000);
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(2000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(2001), Eq(3000));
+
+    tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000));
+
+    tracker.setRenderRate(Fps::fromPeriodNsecs(4000), /*applyImmediately*/ false);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(9000));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues