Merge "Move InputConsumer into separate files" into main
diff --git a/include/android/input_transfer_token_jni.h b/include/android/input_transfer_token_jni.h
new file mode 100644
index 0000000..ba5f6f2
--- /dev/null
+++ b/include/android/input_transfer_token_jni.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+/**
+ * @file input_transfer_token_jni.h
+ */
+#ifndef ANDROID_INPUT_TRANSFER_TOKEN_JNI_H
+#define ANDROID_INPUT_TRANSFER_TOKEN_JNI_H
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+struct AInputTransferToken;
+
+/**
+ * AInputTransferToken can be used to request focus on or to transfer touch gesture to and from
+ * an embedded SurfaceControl
+ */
+typedef struct AInputTransferToken AInputTransferToken;
+
+/**
+ * Return the AInputTransferToken wrapped by a Java InputTransferToken object. This must be released
+ * using AInputTransferToken_release
+ *
+ * inputTransferTokenObj must be a non-null instance of android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+AInputTransferToken* _Nonnull AInputTransferToken_fromJava(JNIEnv* _Nonnull env,
+        jobject _Nonnull inputTransferTokenObj) __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Return the Java InputTransferToken object that wraps AInputTransferToken
+ *
+ * aInputTransferToken must be non null and the returned value is an object of instance
+ * android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+jobject _Nonnull AInputTransferToken_toJava(JNIEnv* _Nonnull env,
+        const AInputTransferToken* _Nonnull aInputTransferToken) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Removes a reference that was previously acquired in native.
+ *
+ * Available since API level 35.
+ */
+void AInputTransferToken_release(AInputTransferToken* _Nonnull aInputTransferToken)
+        __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
+#endif // ANDROID_INPUT_TRANSFER_TOKEN_JNI_H
+/** @} */
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index bf1f2e9..082387e 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -346,7 +346,7 @@
  */
 void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
                                    ASurfaceControl* surface_control, AHardwareBuffer* buffer,
-                                   int acquire_fence_fd = -1) __INTRODUCED_IN(29);
+                                   int acquire_fence_fd) __INTRODUCED_IN(29);
 
 /**
  * Updates the color for \a surface_control.  This will make the background color for the
diff --git a/include/input/Input.h b/include/input/Input.h
index ddc3768..19f4ab3 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -1192,15 +1192,17 @@
  */
 struct PointerCaptureRequest {
 public:
-    inline PointerCaptureRequest() : enable(false), seq(0) {}
-    inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+    inline PointerCaptureRequest() : window(), seq(0) {}
+    inline PointerCaptureRequest(sp<IBinder> window, uint32_t seq) : window(window), seq(seq) {}
     inline bool operator==(const PointerCaptureRequest& other) const {
-        return enable == other.enable && seq == other.seq;
+        return window == other.window && seq == other.seq;
     }
-    explicit inline operator bool() const { return enable; }
+    inline bool isEnable() const { return window != nullptr; }
 
-    // True iff this is a request to enable Pointer Capture.
-    bool enable;
+    // The requesting window.
+    // If the request is to enable the capture, this is the input token of the window that requested
+    // pointer capture. Otherwise, this is nullptr.
+    sp<IBinder> window;
 
     // The sequence number for the request.
     uint32_t seq;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 84ff9d7..ca9b08f 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -159,6 +159,11 @@
                 "UtilsHost.cpp",
             ],
         },
+        android: {
+            lto: {
+                 thin: true,
+            },
+        },
     },
 
     aidl: {
@@ -219,9 +224,6 @@
         "-performance-move-const-arg", // b/273486801
         "portability*",
     ],
-    lto: {
-        thin: true,
-    },
 }
 
 cc_library_headers {
diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h
new file mode 100644
index 0000000..6530b50
--- /dev/null
+++ b/libs/gui/include/gui/InputTransferToken.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <private/gui/ParcelUtils.h>
+#include <utils/Errors.h>
+
+namespace android {
+struct InputTransferToken : public RefBase, Parcelable {
+public:
+    InputTransferToken() { mToken = new BBinder(); }
+
+    InputTransferToken(const sp<IBinder>& token) { mToken = token; }
+
+    status_t writeToParcel(Parcel* parcel) const override {
+        SAFE_PARCEL(parcel->writeStrongBinder, mToken);
+        return NO_ERROR;
+    }
+
+    status_t readFromParcel(const Parcel* parcel) {
+        SAFE_PARCEL(parcel->readStrongBinder, &mToken);
+        return NO_ERROR;
+    };
+
+    sp<IBinder> mToken;
+};
+
+static inline bool operator==(const sp<InputTransferToken>& token1,
+                              const sp<InputTransferToken>& token2) {
+    if (token1.get() == token2.get()) {
+        return true;
+    }
+    return token1->mToken == token2->mToken;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index ff9d9a9..61a964e 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -1034,7 +1034,8 @@
                         (splitPointerProperties.size() * (historySize + 1)));
 
     if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) {
-        LOG(FATAL) << "Cannot split MotionEvent: Requested splitting " << splitCount
+        // TODO(b/329107108): Promote this to a fatal check once bugs in the caller are resolved.
+        LOG(ERROR) << "Cannot split MotionEvent: Requested splitting " << splitCount
                    << " pointers from the original event, but the original event only contained "
                    << splitPointerProperties.size() << " of those pointers.";
     }
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index bdec5c3..b48b0fb 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -87,6 +87,7 @@
 
 flag {
   name: "override_key_behavior_permission_apis"
+  is_exported: true
   namespace: "input"
   description: "enable override key behavior permission APIs"
   bug: "309018874"
@@ -115,6 +116,7 @@
 
 flag {
   name: "input_device_view_behavior_api"
+  is_exported: true
   namespace: "input"
   description: "Controls the API to provide InputDevice view behavior."
   bug: "246946631"
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
index c7fb8bd..19d1eb6 100644
--- a/libs/tracing_perfetto/tracing_perfetto.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -131,7 +131,8 @@
 }
 
 uint64_t getEnabledCategories() {
-  if (internal::isPerfettoSdkTracingEnabled()) {
+  if (internal::isPerfettoRegistered()) {
+    // TODO(b/303199244): Return only enabled categories and not all registered ones
     return internal::getDefaultCategories();
   } else {
     return atrace_get_enabled_tags();
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
index 58ba428..976db7e 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -70,6 +70,8 @@
 
 PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES);
 
+std::atomic_bool is_perfetto_registered = false;
+
 struct PerfettoTeCategory* toCategory(uint64_t inCategory) {
   switch (inCategory) {
     case TRACE_CATEGORY_ALWAYS:
@@ -135,15 +137,11 @@
 
 }  // namespace
 
-bool isPerfettoSdkTracingEnabled() {
-  return android::os::perfetto_sdk_tracing();
+bool isPerfettoRegistered() {
+  return is_perfetto_registered;
 }
 
 struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) {
-  if (!isPerfettoSdkTracingEnabled()) {
-    return nullptr;
-  }
-
   struct PerfettoTeCategory* perfettoCategory = toCategory(category);
   bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
       (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
@@ -151,9 +149,10 @@
 }
 
 void registerWithPerfetto(bool test) {
-  if (!isPerfettoSdkTracingEnabled()) {
+  if (!android::os::perfetto_sdk_tracing()) {
     return;
   }
+
   static std::once_flag registration;
   std::call_once(registration, [test]() {
     struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
@@ -161,6 +160,7 @@
     PerfettoProducerInit(args);
     PerfettoTeInit();
     PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
+    is_perfetto_registered = true;
   });
 }
 
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
index 9a579f1..79e4b8f 100644
--- a/libs/tracing_perfetto/tracing_perfetto_internal.h
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -26,7 +26,7 @@
 
 namespace internal {
 
-bool isPerfettoSdkTracingEnabled();
+bool isPerfettoRegistered();
 
 struct PerfettoTeCategory* toPerfettoCategory(uint64_t category);
 
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 9db3574..c333814 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -122,21 +122,32 @@
     }
 
     auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
+    NotifyMotionArgs newArgs(args);
+    newArgs.displayId = displayId;
 
-    const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
-    const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
-    pc.move(deltaX, deltaY);
+    if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
+        // This is an absolute mouse device that knows about the location of the cursor on the
+        // display, so set the cursor position to the specified location.
+        const auto [x, y] = pc.getPosition();
+        const float deltaX = args.xCursorPosition - x;
+        const float deltaY = args.yCursorPosition - y;
+        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
+        pc.setPosition(args.xCursorPosition, args.yCursorPosition);
+    } else {
+        // This is a relative mouse, so move the cursor by the specified amount.
+        const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+        const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+        pc.move(deltaX, deltaY);
+        const auto [x, y] = pc.getPosition();
+        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        newArgs.xCursorPosition = x;
+        newArgs.yCursorPosition = y;
+    }
     if (canUnfadeOnDisplay(displayId)) {
         pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
     }
-
-    const auto [x, y] = pc.getPosition();
-    NotifyMotionArgs newArgs(args);
-    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-    newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-    newArgs.xCursorPosition = x;
-    newArgs.yCursorPosition = y;
-    newArgs.displayId = displayId;
     return newArgs;
 }
 
@@ -277,7 +288,7 @@
 
 void PointerChoreographer::notifyPointerCaptureChanged(
         const NotifyPointerCaptureChangedArgs& args) {
-    if (args.request.enable) {
+    if (args.request.isEnable()) {
         std::scoped_lock _l(mLock);
         for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
             mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 264dc03..0246d60 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -110,7 +110,7 @@
 
 std::string PointerCaptureChangedEntry::getDescription() const {
     return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
-                        pointerCaptureRequest.enable ? "true" : "false");
+                        pointerCaptureRequest.isEnable() ? "true" : "false");
 }
 
 // --- DragEntry ---
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 16875df..dc220fe 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1715,7 +1715,7 @@
     const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
     sp<IBinder> token;
 
-    if (entry->pointerCaptureRequest.enable) {
+    if (entry->pointerCaptureRequest.isEnable()) {
         // Enable Pointer Capture.
         if (haveWindowWithPointerCapture &&
             (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
@@ -1724,7 +1724,7 @@
             ALOGI("Skipping dispatch of Pointer Capture being enabled: no state change.");
             return;
         }
-        if (!mCurrentPointerCaptureRequest.enable) {
+        if (!mCurrentPointerCaptureRequest.isEnable()) {
             // This can happen if a window requests capture and immediately releases capture.
             ALOGW("No window requested Pointer Capture.");
             dropReason = DropReason::NO_POINTER_CAPTURE;
@@ -1737,6 +1737,8 @@
 
         token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
         LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
+        LOG_ALWAYS_FATAL_IF(token != entry->pointerCaptureRequest.window,
+                            "Unexpected requested window for Pointer Capture.");
         mWindowTokenWithPointerCapture = token;
     } else {
         // Disable Pointer Capture.
@@ -1756,8 +1758,8 @@
         }
         token = mWindowTokenWithPointerCapture;
         mWindowTokenWithPointerCapture = nullptr;
-        if (mCurrentPointerCaptureRequest.enable) {
-            setPointerCaptureLocked(false);
+        if (mCurrentPointerCaptureRequest.isEnable()) {
+            setPointerCaptureLocked(nullptr);
         }
     }
 
@@ -1765,8 +1767,8 @@
     if (connection == nullptr) {
         // Window has gone away, clean up Pointer Capture state.
         mWindowTokenWithPointerCapture = nullptr;
-        if (mCurrentPointerCaptureRequest.enable) {
-            setPointerCaptureLocked(false);
+        if (mCurrentPointerCaptureRequest.isEnable()) {
+            setPointerCaptureLocked(nullptr);
         }
         return;
     }
@@ -3832,9 +3834,10 @@
             case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
                 const auto& captureEntry =
                         static_cast<const PointerCaptureChangedEntry&>(eventEntry);
-                status = connection->inputPublisher
-                                 .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
-                                                      captureEntry.pointerCaptureRequest.enable);
+                status =
+                        connection->inputPublisher
+                                .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
+                                                     captureEntry.pointerCaptureRequest.isEnable());
                 break;
             }
 
@@ -4357,6 +4360,20 @@
             MotionEvent::split(originalMotionEntry.action, originalMotionEntry.flags,
                                /*historySize=*/0, originalMotionEntry.pointerProperties,
                                originalMotionEntry.pointerCoords, pointerIds);
+    if (pointerIds.count() != pointerCoords.size()) {
+        // TODO(b/329107108): Determine why some IDs in pointerIds were not in originalMotionEntry.
+        // This is bad.  We are missing some of the pointers that we expected to deliver.
+        // Most likely this indicates that we received an ACTION_MOVE events that has
+        // different pointer ids than we expected based on the previous ACTION_DOWN
+        // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
+        // in this way.
+        ALOGW("Dropping split motion event because the pointer count is %d but "
+              "we expected there to be %zu pointers.  This probably means we received "
+              "a broken sequence of pointer ids from the input device: %s",
+              pointerCoords.size(), pointerIds.count(),
+              originalMotionEntry.getDescription().c_str());
+        return nullptr;
+    }
 
     // TODO(b/327503168): Move this check inside MotionEvent::split once all callers handle it
     //   correctly.
@@ -4713,7 +4730,7 @@
 void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
     if (debugInboundEventDetails()) {
         ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime,
-              args.request.enable ? "true" : "false");
+              args.request.isEnable() ? "true" : "false");
     }
 
     bool needWake = false;
@@ -5784,7 +5801,7 @@
     std::string dump;
 
     dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
-                         toString(mCurrentPointerCaptureRequest.enable));
+                         toString(mCurrentPointerCaptureRequest.isEnable()));
 
     std::string windowName = "None";
     if (mWindowTokenWithPointerCapture) {
@@ -6203,7 +6220,7 @@
             return;
         }
 
-        if (enabled == mCurrentPointerCaptureRequest.enable) {
+        if (enabled == mCurrentPointerCaptureRequest.isEnable()) {
             ALOGW("Ignoring request to %s Pointer Capture: "
                   "window has %s requested pointer capture.",
                   enabled ? "enable" : "disable", enabled ? "already" : "not");
@@ -6219,7 +6236,7 @@
             }
         }
 
-        setPointerCaptureLocked(enabled);
+        setPointerCaptureLocked(enabled ? windowToken : nullptr);
     } // release lock
 
     // Wake the thread to process command entries.
@@ -6849,14 +6866,14 @@
 }
 
 void InputDispatcher::disablePointerCaptureForcedLocked() {
-    if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
+    if (!mCurrentPointerCaptureRequest.isEnable() && !mWindowTokenWithPointerCapture) {
         return;
     }
 
     ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
 
-    if (mCurrentPointerCaptureRequest.enable) {
-        setPointerCaptureLocked(false);
+    if (mCurrentPointerCaptureRequest.isEnable()) {
+        setPointerCaptureLocked(nullptr);
     }
 
     if (!mWindowTokenWithPointerCapture) {
@@ -6876,8 +6893,8 @@
     mInboundQueue.push_front(std::move(entry));
 }
 
-void InputDispatcher::setPointerCaptureLocked(bool enable) {
-    mCurrentPointerCaptureRequest.enable = enable;
+void InputDispatcher::setPointerCaptureLocked(const sp<IBinder>& windowToken) {
+    mCurrentPointerCaptureRequest.window = windowToken;
     mCurrentPointerCaptureRequest.seq++;
     auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
         scoped_unlock unlock(mLock);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d6eba64..13571b3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -421,7 +421,8 @@
     void disablePointerCaptureForcedLocked() REQUIRES(mLock);
 
     // Set the Pointer Capture state in the Policy.
-    void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
+    // The window is not nullptr for requests to enable, otherwise it is nullptr.
+    void setPointerCaptureLocked(const sp<IBinder>& window) REQUIRES(mLock);
 
     // Dispatcher state at time of last ANR.
     std::string mLastAnrState GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 83ed452..49e6e21 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -183,10 +183,21 @@
     const int32_t windowId = dispatchEntry.windowId.value_or(0);
     const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
 
-    mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
-                                   dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
-                                   windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
-                                   /*hmac=*/{}, resolvedKeyRepeatCount});
+    const WindowDispatchArgs windowDispatchArgs{std::move(traced),
+                                                dispatchEntry.deliveryTime,
+                                                dispatchEntry.resolvedFlags,
+                                                dispatchEntry.targetUid,
+                                                vsyncId,
+                                                windowId,
+                                                dispatchEntry.transform,
+                                                dispatchEntry.rawTransform,
+                                                /*hmac=*/{},
+                                                resolvedKeyRepeatCount};
+    if (eventState->isEventProcessingComplete) {
+        mBackend->traceWindowDispatch(std::move(windowDispatchArgs));
+    } else {
+        eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs));
+    }
 }
 
 std::shared_ptr<InputTracer::EventState>& InputTracer::getState(
@@ -205,6 +216,12 @@
     for (const auto& event : events) {
         writeEventToBackend(event, *tracer.mBackend);
     }
+    // Write all pending dispatch args to the trace.
+    for (const auto& windowDispatchArgs : pendingDispatchArgs) {
+        tracer.mBackend->traceWindowDispatch(windowDispatchArgs);
+    }
+    pendingDispatchArgs.clear();
+
     isEventProcessingComplete = true;
 }
 
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 529c0fa..8da9632 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -52,6 +52,8 @@
 private:
     std::unique_ptr<InputTracingBackendInterface> mBackend;
 
+    using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
+
     // The state of a tracked event, shared across all events derived from the original event.
     struct EventState {
         explicit inline EventState(InputTracer& tracer) : tracer(tracer){};
@@ -62,6 +64,8 @@
         InputTracer& tracer;
         std::vector<const TracedEvent> events;
         bool isEventProcessingComplete{false};
+        // A queue to hold dispatch args from being traced until event processing is complete.
+        std::vector<const WindowDispatchArgs> pendingDispatchArgs;
         // TODO(b/210460522): Add additional args for tracking event sensitivity and
         //  dispatch target UIDs.
     };
diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
index e4363a4..8ffbc11 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -107,7 +107,9 @@
 
         // Set mouse cursor position for the most common cases to avoid boilerplate.
         if (mSource == AINPUT_SOURCE_MOUSE &&
-            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+            !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+            BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_X) &&
+            BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_Y)) {
             mRawXCursorPosition = pointerCoords[0].getX();
             mRawYCursorPosition = pointerCoords[0].getY();
         }
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 06f10e5..c8cc5dc 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -486,7 +486,7 @@
 }
 
 void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration& config) {
-    if (config.pointerCaptureRequest.enable) {
+    if (config.pointerCaptureRequest.isEnable()) {
         if (mParameters.mode == Parameters::Mode::POINTER) {
             mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
             mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 3c26d1d..7d27d4a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -923,7 +923,7 @@
 
     // Determine device mode.
     if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
-        mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
+        mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.isEnable()) {
         mSource = AINPUT_SOURCE_MOUSE;
         mDeviceMode = DeviceMode::POINTER;
         if (hasStylus()) {
@@ -1038,7 +1038,7 @@
             (mDeviceMode == DeviceMode::POINTER) ||
             // - when pointer capture is enabled, to preserve the mouse cursor position;
             (mParameters.deviceType == Parameters::DeviceType::POINTER &&
-             mConfig.pointerCaptureRequest.enable) ||
+             mConfig.pointerCaptureRequest.isEnable()) ||
             // - when we should be showing touches;
             (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
             // - when we should be showing a pointer icon for direct styluses.
@@ -1047,7 +1047,7 @@
         if (mPointerController == nullptr) {
             mPointerController = getContext()->getPointerController(getDeviceId());
         }
-        if (mConfig.pointerCaptureRequest.enable) {
+        if (mConfig.pointerCaptureRequest.isEnable()) {
             mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
         }
     } else {
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index eacc66e..99f9e24 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -410,9 +410,9 @@
                 .setBoolValues({config.touchpadRightClickZoneEnabled});
     }
     std::list<NotifyArgs> out;
-    if ((!changes.any() && config.pointerCaptureRequest.enable) ||
+    if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
         changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
-        mPointerCaptured = config.pointerCaptureRequest.enable;
+        mPointerCaptured = config.pointerCaptureRequest.isEnable();
         // The motion ranges are going to change, so bump the generation to clear the cached ones.
         bumpGeneration();
         if (mPointerCaptured) {
diff --git a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
index 81b4968..26028c5 100644
--- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
@@ -26,15 +26,30 @@
 
 extern "C" {
 
+namespace {
+
+/**
+ * Log details of each gesture output by the gestures library.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
+ * restarting the shell)
+ */
+const bool DEBUG_TOUCHPAD_GESTURES =
+        __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+                                  ANDROID_LOG_INFO);
+
+} // namespace
+
 void gestures_log(int verb, const char* fmt, ...) {
     va_list args;
     va_start(args, fmt);
     if (verb == GESTURES_LOG_ERROR) {
         LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
-    } else if (verb == GESTURES_LOG_INFO) {
-        LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
-    } else {
-        LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+    } else if (DEBUG_TOUCHPAD_GESTURES) {
+        if (verb == GESTURES_LOG_INFO) {
+            LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+        } else {
+            LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+        }
     }
     va_end(args);
 }
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index de74067..c44f880 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -166,7 +166,7 @@
     }
 
     void setPointerCapture(bool enabled) {
-        mReaderConfiguration.pointerCaptureRequest.enable = enabled;
+        mReaderConfiguration.pointerCaptureRequest.window = enabled ? sp<BBinder>::make() : nullptr;
         mReaderConfiguration.pointerCaptureRequest.seq = 1;
         int32_t generation = mDevice->getGeneration();
         std::list<NotifyArgs> args =
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 9e93712..8f593b5 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -178,8 +178,8 @@
     transform = t;
 }
 
-PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) {
-    mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(const sp<IBinder>& window) {
+    mConfig.pointerCaptureRequest = {window, mNextPointerCaptureSequenceNumber++};
     return mConfig.pointerCaptureRequest;
 }
 
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index da5085d..710bb54 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -68,7 +68,7 @@
     TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
                                                            ui::Rotation surfaceRotation);
     void setTouchAffineTransformation(const TouchAffineTransformation t);
-    PointerCaptureRequest setPointerCapture(bool enabled);
+    PointerCaptureRequest setPointerCapture(const sp<IBinder>& window);
     void setShowTouches(bool enabled);
     void setDefaultPointerDisplayId(int32_t pointerDisplayId);
     void setPointerGestureEnabled(bool enabled);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 423975f..9375e92 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -309,17 +309,22 @@
                    "signal";
     }
 
-    PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) {
+    PointerCaptureRequest assertSetPointerCaptureCalled(const sp<WindowInfoHandle>& window,
+                                                        bool enabled) {
         std::unique_lock lock(mLock);
         base::ScopedLockAssertion assumeLocked(mLock);
 
-        if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
-                                                      [this, enabled]() REQUIRES(mLock) {
-                                                          return mPointerCaptureRequest->enable ==
-                                                                  enabled;
-                                                      })) {
-            ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled
-                          << ") to be called.";
+        if (!mPointerCaptureChangedCondition
+                     .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) {
+                         if (enabled) {
+                             return mPointerCaptureRequest->isEnable() &&
+                                     mPointerCaptureRequest->window == window->getToken();
+                         } else {
+                             return !mPointerCaptureRequest->isEnable();
+                         }
+                     })) {
+            ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", "
+                          << enabled << ") to be called.";
             return {};
         }
         auto request = *mPointerCaptureRequest;
@@ -334,7 +339,7 @@
         if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
             FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
                       "enabled = "
-                   << std::to_string(mPointerCaptureRequest->enable);
+                   << std::to_string(mPointerCaptureRequest->isEnable());
         }
         mPointerCaptureRequest.reset();
     }
@@ -9889,7 +9894,7 @@
     PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
                                                          bool enabled) {
         mDispatcher->requestPointerCapture(window->getToken(), enabled);
-        auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+        auto request = mFakePolicy->assertSetPointerCaptureCalled(window, enabled);
         notifyPointerCaptureChanged(request);
         window->consumeCaptureEvent(enabled);
         return request;
@@ -9922,7 +9927,7 @@
     mWindow->consumeCaptureEvent(false);
     mWindow->consumeFocusEvent(false);
     mSecondWindow->consumeFocusEvent(true);
-    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
 
     // Ensure that additional state changes from InputReader are not sent to the window.
     notifyPointerCaptureChanged({});
@@ -9941,7 +9946,7 @@
     notifyPointerCaptureChanged(request);
 
     // Ensure that Pointer Capture is disabled.
-    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
     mWindow->consumeCaptureEvent(false);
     mWindow->assertNoEvents();
 }
@@ -9951,13 +9956,13 @@
 
     // The first window loses focus.
     setFocusedWindow(mSecondWindow);
-    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
     mWindow->consumeCaptureEvent(false);
 
     // Request Pointer Capture from the second window before the notification from InputReader
     // arrives.
     mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
-    auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
+    auto request = mFakePolicy->assertSetPointerCaptureCalled(mSecondWindow, true);
 
     // InputReader notifies Pointer Capture was disabled (because of the focus change).
     notifyPointerCaptureChanged({});
@@ -9972,11 +9977,11 @@
 TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
     // App repeatedly enables and disables capture.
     mDispatcher->requestPointerCapture(mWindow->getToken(), true);
-    auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+    auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
     mDispatcher->requestPointerCapture(mWindow->getToken(), false);
-    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
     mDispatcher->requestPointerCapture(mWindow->getToken(), true);
-    auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+    auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
 
     // InputReader notifies that PointerCapture has been enabled for the first request. Since the
     // first request is now stale, this should do nothing.
@@ -9993,10 +9998,10 @@
 
     // App toggles pointer capture off and on.
     mDispatcher->requestPointerCapture(mWindow->getToken(), false);
-    mFakePolicy->assertSetPointerCaptureCalled(false);
+    mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
 
     mDispatcher->requestPointerCapture(mWindow->getToken(), true);
-    auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+    auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
 
     // InputReader notifies that the latest "enable" request was processed, while skipping over the
     // preceding "disable" request.
@@ -10048,6 +10053,26 @@
     mWindow->assertNoEvents();
 }
 
+using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests;
+
+TEST_F(InputDispatcherPointerCaptureDeathTest,
+       NotifyPointerCaptureChangedWithWrongTokenAbortsDispatcher) {
+    testing::GTEST_FLAG(death_test_style) = "threadsafe";
+    ScopedSilentDeath _silentDeath;
+
+    mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+    auto request = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
+
+    // Dispatch a pointer changed event with a wrong token.
+    request.window = mSecondWindow->getToken();
+    ASSERT_DEATH(
+            {
+                notifyPointerCaptureChanged(request);
+                mSecondWindow->consumeCaptureEvent(true);
+            },
+            "Unexpected requested window for Pointer Capture.");
+}
+
 class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
 protected:
     constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 835f8b8..1d46c9a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1165,18 +1165,18 @@
 TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
     NotifyPointerCaptureChangedArgs args;
 
-    auto request = mFakePolicy->setPointerCapture(true);
+    auto request = mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
     mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+    ASSERT_TRUE(args.request.isEnable()) << "Pointer Capture should be enabled.";
     ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
 
-    mFakePolicy->setPointerCapture(false);
+    mFakePolicy->setPointerCapture(/*window=*/nullptr);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
     mReader->loopOnce();
     mFakeListener->assertNotifyCaptureWasCalled(&args);
-    ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
+    ASSERT_FALSE(args.request.isEnable()) << "Pointer Capture should be disabled.";
 
     // Verify that the Pointer Capture state is not updated when the configuration value
     // does not change.
@@ -9802,7 +9802,7 @@
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    mFakePolicy->setPointerCapture(true);
+    mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
     mFakePolicy->setPointerController(fakePointerController);
     MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
@@ -9934,7 +9934,7 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
 
     // non captured touchpad should be a mouse source
-    mFakePolicy->setPointerCapture(false);
+    mFakePolicy->setPointerCapture(/*window=*/nullptr);
     configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
@@ -10012,14 +10012,14 @@
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
     mFakePolicy->setPointerController(fakePointerController);
-    mFakePolicy->setPointerCapture(false);
+    mFakePolicy->setPointerCapture(/*window=*/nullptr);
     MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
 
     // uncaptured touchpad should be a pointer device
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
     // captured touchpad should be a touchpad device
-    mFakePolicy->setPointerCapture(true);
+    mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
     configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
     ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
@@ -10090,7 +10090,7 @@
         prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
         // In order to enable swipe and freeform gesture in pointer mode, pointer capture
         // needs to be disabled, and the pointer gesture needs to be enabled.
-        mFakePolicy->setPointerCapture(false);
+        mFakePolicy->setPointerCapture(/*window=*/nullptr);
         mFakePolicy->setPointerGestureEnabled(true);
         mFakePolicy->setPointerController(fakePointerController);
 
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index e9e5061..8ddb672 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -355,6 +355,38 @@
             AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
 }
 
+TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    // Set initial position of the PointerController.
+    pc->setPosition(100, 200);
+    const auto absoluteMousePointer = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                              .axis(AMOTION_EVENT_AXIS_X, 110)
+                                              .axis(AMOTION_EVENT_AXIS_Y, 220);
+
+    // Make NotifyMotionArgs and notify Choreographer.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(absoluteMousePointer)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+
+    // Check that the PointerController updated the position and the pointer is shown.
+    pc->assertPosition(110, 220);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Check that x-y coordinates, displayId and cursor position are correctly updated.
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithCoords(110, 220), WithRelativeMotion(10, 20), WithDisplayId(DISPLAY_ID),
+                  WithCursorPosition(110, 220)));
+}
+
 TEST_F(PointerChoreographerTest,
        AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
     // Add two displays and set one to default.
@@ -413,7 +445,8 @@
              {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}});
     mChoreographer.notifyPointerCaptureChanged(
             NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
-                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+                                            PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+                                                                  /*seq=*/0)));
 
     // Notify motion as if pointer capture is enabled.
     mChoreographer.notifyMotion(
@@ -450,7 +483,8 @@
     // Enable pointer capture and check if the PointerController hid the pointer.
     mChoreographer.notifyPointerCaptureChanged(
             NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
-                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+                                            PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+                                                                  /*seq=*/0)));
     ASSERT_FALSE(pc->isPointerShown());
 }
 
@@ -1295,7 +1329,8 @@
     // Assume that pointer capture is enabled.
     mChoreographer.notifyPointerCaptureChanged(
             NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
-                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+                                            PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+                                                                  /*seq=*/0)));
 
     // Notify motion as if pointer capture is enabled.
     mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD)
@@ -1329,7 +1364,8 @@
     // Enable pointer capture and check if the PointerController hid the pointer.
     mChoreographer.notifyPointerCaptureChanged(
             NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
-                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+                                            PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+                                                                  /*seq=*/0)));
     ASSERT_FALSE(pc->isPointerShown());
 }
 
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index c2bf275..643e8b9 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -125,6 +125,9 @@
     config.touchpadTapToClickEnabled = fdp.ConsumeBool();
     config.touchpadTapDraggingEnabled = fdp.ConsumeBool();
     config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
+
+    config.pointerCaptureRequest.window = fdp.ConsumeBool() ? sp<BBinder>::make() : nullptr;
+    config.pointerCaptureRequest.seq = fdp.ConsumeIntegral<uint32_t>();
 }
 
 } // namespace
@@ -145,7 +148,6 @@
     // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
     // TouchpadInputMapper constructor.
     setTouchpadSettings(*fdp, policyConfig);
-    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
     TouchpadInputMapper& mapper =
             getMapperForDevice<ThreadSafeFuzzedDataProvider, TouchpadInputMapper>(*fdp, device,
                                                                                   policyConfig);
@@ -164,7 +166,6 @@
                 [&]() -> void { mapper.getSources(); },
                 [&]() -> void {
                     setTouchpadSettings(*fdp, policyConfig);
-                    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
                     std::list<NotifyArgs> unused =
                             mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
                                                InputReaderConfiguration::Change(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 18a96f4..843b5c5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -19,6 +19,7 @@
 #include <chrono>
 #include <optional>
 #include <vector>
+#include "utils/Timers.h"
 
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFE.h>
@@ -105,6 +106,9 @@
     bool hasTrustedPresentationListener = false;
 
     ICEPowerCallback* powerCallback = nullptr;
+
+    // System time for when frame refresh starts. Used for stats.
+    nsecs_t refreshStartTime = 0;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a1d6132..a499928 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -58,8 +58,7 @@
     // Called before composition starts. Should return true if this layer has
     // pending updates which would require an extra display refresh cycle to
     // process.
-    virtual bool onPreComposition(nsecs_t refreshStartTime,
-                                  bool updatingOutputGeometryThisFrame) = 0;
+    virtual bool onPreComposition(bool updatingOutputGeometryThisFrame) = 0;
 
     struct ClientCompositionTargetSettings {
         enum class BlurSetting {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 15e4577..1b8cc27 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -43,7 +43,7 @@
 
     MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
 
-    MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool));
+    MOCK_METHOD1(onPreComposition, bool(bool));
 
     MOCK_CONST_METHOD1(prepareClientComposition,
                        std::optional<compositionengine::LayerFE::LayerSettings>(
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index d87eae3..b470208 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -181,10 +181,10 @@
 
     bool needsAnotherUpdate = false;
 
-    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    mRefreshStartTime = args.refreshStartTime;
 
     for (auto& layer : args.layers) {
-        if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) {
+        if (layer->onPreComposition(args.updatingOutputGeometryThisFrame)) {
             needsAnotherUpdate = true;
         }
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index da578e2..042010e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -214,6 +214,7 @@
 
 TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
     const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
+    mRefreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
     mEngine.preComposition(mRefreshArgs);
     const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
 
@@ -226,12 +227,9 @@
     nsecs_t ts1 = 0;
     nsecs_t ts2 = 0;
     nsecs_t ts3 = 0;
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _))
-            .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _))
-            .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _))
-            .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -245,9 +243,9 @@
 }
 
 TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
 
     mEngine.setNeedsAnotherUpdateForTest(true);
 
@@ -262,9 +260,9 @@
 
 TEST_F(CompositionTestPreComposition,
        preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 8dfbeb8..faa5197 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -48,6 +48,7 @@
 using aidl::android::hardware::power::Boost;
 using aidl::android::hardware::power::Mode;
 using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionTag;
 using aidl::android::hardware::power::WorkDuration;
 
 PowerAdvisor::~PowerAdvisor() = default;
@@ -204,13 +205,36 @@
     return *mSupportsHintSession;
 }
 
+bool PowerAdvisor::shouldCreateSessionWithConfig() {
+    return mSessionConfigSupported && FlagManager::getInstance().adpf_use_fmq_channel();
+}
+
 bool PowerAdvisor::ensurePowerHintSessionRunning() {
     if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
-        auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
-                                                   mHintSessionThreadIds, mTargetDuration.ns());
-
-        if (ret.isOk()) {
-            mHintSession = ret.value();
+        if (shouldCreateSessionWithConfig()) {
+            auto ret = getPowerHal().createHintSessionWithConfig(getpid(),
+                                                                 static_cast<int32_t>(getuid()),
+                                                                 mHintSessionThreadIds,
+                                                                 mTargetDuration.ns(),
+                                                                 SessionTag::SURFACEFLINGER,
+                                                                 &mSessionConfig);
+            if (ret.isOk()) {
+                mHintSession = ret.value();
+            }
+            // If it fails the first time we try, or ever returns unsupported, assume unsupported
+            else if (mFirstConfigSupportCheck || ret.isUnsupported()) {
+                ALOGI("Hint session with config is unsupported, falling back to a legacy session");
+                mSessionConfigSupported = false;
+            }
+            mFirstConfigSupportCheck = false;
+        }
+        // Immediately try original method after, in case the first way returned unsupported
+        if (mHintSession == nullptr && !shouldCreateSessionWithConfig()) {
+            auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+                                                       mHintSessionThreadIds, mTargetDuration.ns());
+            if (ret.isOk()) {
+                mHintSession = ret.value();
+            }
         }
     }
     return mHintSession != nullptr;
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 1040048..13e1263 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -230,6 +230,9 @@
     // this normalizes them together and takes the max of the two
     Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
 
+    // Whether to use the new "createHintSessionWithConfig" method
+    bool shouldCreateSessionWithConfig() REQUIRES(mHintSessionMutex);
+
     bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
     std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
 
@@ -278,6 +281,13 @@
     std::promise<bool> mDelayReportActualMutexAcquisitonPromise;
     bool mTimingTestingMode = false;
 
+    // Hint session configuration data
+    aidl::android::hardware::power::SessionConfig mSessionConfig;
+
+    // Whether createHintSessionWithConfig is supported, assume true until it fails
+    bool mSessionConfigSupported = true;
+    bool mFirstConfigSupportCheck = true;
+
     // Whether we should emit ATRACE_INT data for hint sessions
     static const bool sTraceHintSessionData;
 
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index cb0e2a1..867f3af 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -163,7 +163,9 @@
     LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
 
     if (clientState.what & layer_state_t::eFlagsChanged) {
-        if ((oldFlags ^ flags) & (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque)) {
+        if ((oldFlags ^ flags) &
+            (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
+             layer_state_t::eLayerSecure)) {
             changes |= RequestedLayerState::Changes::Visibility |
                     RequestedLayerState::Changes::VisibleRegion;
         }
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 2dbcb84..43a4397 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -82,8 +82,7 @@
     return mSnapshot.get();
 }
 
-bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) {
-    mCompositionResult.refreshStartTime = refreshStartTime;
+bool LayerFE::onPreComposition(bool) {
     return mSnapshot->hasReadyFrame;
 }
 
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index d584fb7..66cb88b 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -26,9 +26,6 @@
 namespace android {
 
 struct CompositionResult {
-    // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
-    // and remove this field.
-    nsecs_t refreshStartTime = 0;
     std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
     sp<Fence> lastClientCompositionFence = nullptr;
 };
@@ -39,7 +36,7 @@
 
     // compositionengine::LayerFE overrides
     const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
+    bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
     void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
     const char* getDebugName() const override;
     int32_t getSequence() const override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 78e14ae..bf210af 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2783,12 +2783,16 @@
         }
     }
 
+    refreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    for (auto [layer, layerFE] : layers) {
+        layer->onPreComposition(refreshArgs.refreshStartTime);
+    }
+
     mCompositionEngine->present(refreshArgs);
     moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 
     for (auto [layer, layerFE] : layers) {
         CompositionResult compositionResult{layerFE->stealCompositionResult()};
-        layer->onPreComposition(compositionResult.refreshStartTime);
         for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
             Layer* clonedFrom = layer->getClonedFrom().get();
             auto owningLayer = clonedFrom ? clonedFrom : layer;
diff --git a/services/surfaceflinger/common/include/common/test/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h
index 550c70d..d61fcb5 100644
--- a/services/surfaceflinger/common/include/common/test/FlagUtils.h
+++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h
@@ -18,7 +18,10 @@
 
 #include <common/FlagManager.h>
 
-#define SET_FLAG_FOR_TEST(name, value) TestFlagSetter _testflag_((name), (name), (value))
+#define SET_FLAG_FOR_TEST(name, value) \
+    TestFlagSetter _testflag_ {        \
+        (name), (name), (value)        \
+    }
 
 namespace android {
 class TestFlagSetter {
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index aecfcba..867ff55 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -560,4 +560,36 @@
               ftl::Flags<RequestedLayerState::Changes>().string());
 }
 
+TEST_F(LayerLifecycleManagerTest, layerSecureChangesSetsVisibilityChangeFlag) {
+    // add a default buffer and make the layer secure
+    setFlags(1, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+    setBuffer(1,
+              std::make_shared<renderengine::mock::
+                                       FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                            1ULL /* bufferId */,
+                                                            HAL_PIXEL_FORMAT_RGBA_8888,
+                                                            GRALLOC_USAGE_SW_READ_NEVER /*usage*/));
+
+    mLifecycleManager.commitChanges();
+
+    // set new buffer but layer secure doesn't change
+    setBuffer(1,
+              std::make_shared<renderengine::mock::
+                                       FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+                                                            2ULL /* bufferId */,
+                                                            HAL_PIXEL_FORMAT_RGBA_8888,
+                                                            GRALLOC_USAGE_SW_READ_NEVER /*usage*/));
+    EXPECT_EQ(mLifecycleManager.getGlobalChanges().get(),
+              ftl::Flags<RequestedLayerState::Changes>(RequestedLayerState::Changes::Buffer |
+                                                       RequestedLayerState::Changes::Content)
+                      .get());
+    mLifecycleManager.commitChanges();
+
+    // change layer flags and confirm visibility flag is set
+    setFlags(1, layer_state_t::eLayerSecure, 0);
+    EXPECT_TRUE(
+            mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+    mLifecycleManager.commitChanges();
+}
+
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 1d44a3e..d9343c7 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -18,7 +18,9 @@
 #define LOG_TAG "PowerAdvisorTest"
 
 #include <DisplayHardware/PowerAdvisor.h>
+#include <android_os.h>
 #include <binder/Status.h>
+#include <common/test/FlagUtils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <powermanager/PowerHalWrapper.h>
@@ -55,6 +57,7 @@
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
     MockPowerHalController* mMockPowerHalController;
     std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, true);
 };
 
 bool PowerAdvisorTest::sessionExists() {
@@ -75,13 +78,14 @@
 void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
     mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
     if (returnValidSession) {
-        ON_CALL(*mMockPowerHalController, createHintSession)
-                .WillByDefault([&](int32_t, int32_t, const std::vector<int32_t>&, int64_t) {
-                    return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
-                            fromStatus(ndk::ScopedAStatus::ok(), mMockPowerHintSession);
-                });
+        ON_CALL(*mMockPowerHalController, createHintSessionWithConfig)
+                .WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{
+                                             .id = 12}),
+                                     Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+                                                    fromStatus(binder::Status::ok(),
+                                                               mMockPowerHintSession))));
     } else {
-        ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] {
+        ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] {
             return HalResult<
                     std::shared_ptr<PowerHintSessionWrapper>>::fromStatus(ndk::ScopedAStatus::ok(),
                                                                           nullptr);
@@ -287,7 +291,7 @@
 }
 
 TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
-    EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).Times(1);
+    EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
     mPowerAdvisor->onBootFinished();
     startPowerHintSession();
     mPowerAdvisor->startPowerHintSession({1, 2, 3});
@@ -339,7 +343,7 @@
         return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127));
     });
 
-    ON_CALL(*mMockPowerHalController, createHintSession).WillByDefault([] {
+    ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] {
         return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
                 fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr);
     });
@@ -374,5 +378,17 @@
     EXPECT_EQ(sessionExists(), false);
 }
 
+TEST_F(PowerAdvisorTest, legacyHintSessionCreationStillWorks) {
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, false);
+    mPowerAdvisor->onBootFinished();
+    mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
+    EXPECT_CALL(*mMockPowerHalController, createHintSession)
+            .Times(1)
+            .WillOnce(Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+                                     fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+    mPowerAdvisor->enablePowerHintSession(true);
+    mPowerAdvisor->startPowerHintSession({1, 2, 3});
+}
+
 } // namespace
 } // namespace android::Hwc2::impl