Merge "Use a strongly typed LogicalDisplayId for displayId(2/n)" into main
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index b1ab7b0..2929bce 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -718,9 +718,17 @@
  *     When registering the interface, add:
  *         std::shared_ptr<MyFoo> foo = new MyFoo; // class in AOSP codebase
  *         std::shared_ptr<MyBar> bar = new MyBar; // custom extension class
- *         ... = AIBinder_setExtension(foo->asBinder().get(), bar->asBinder().get());
+ *         SpAIBinder binder = foo->asBinder(); // target binder to extend
+ *         ... = AIBinder_setExtension(binder.get(), bar->asBinder().get());
+ *         ... = AServiceManager_addService(binder.get(), instanceName);
  *         // handle error
  *
+ *         Do not use foo->asBinder().get() as the target binder argument to
+ *         AIBinder_setExtensions because asBinder it creates a new binder
+ *         object that will be destroyed after the function is called. The same
+ *         binder object must be used for AIBinder_setExtension and
+ *         AServiceManager_addService to register the service with an extension.
+ *
  *     Then, clients of IFoo can get this extension:
  *         SpAIBinder binder = ...;
  *         std::shared_ptr<IFoo> foo = IFoo::fromBinder(binder); // handle if null
diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
index 73a15be..82f528e 100644
--- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
+++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//!
-
 use crate::{
     buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher,
     BufferSubscriber, Frame, StreamConfig,
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 5c4b889..560166c 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -16,13 +16,6 @@
 }
 
 flag {
-  name: "enable_pointer_choreographer"
-  namespace: "input"
-  description: "Set to true to enable PointerChoreographer: the new pipeline for showing pointer icons"
-  bug: "293587049"
-}
-
-flag {
   name: "enable_gestures_library_timer_provider"
   namespace: "input"
   description: "Set to true to enable timer support for the touchpad Gestures library"
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
index 8d43f79..f20b213 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -13,4 +13,11 @@
   namespace: "sensors"
   description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not."
   bug: "316958439"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "sensor_event_connection_send_event_require_nonnull_scratch"
+  namespace: "sensors"
+  description: "This flag controls we allow to pass in nullptr as scratch in SensorEventConnection::sendEvents()"
+  bug: "339306599"
+}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 96d5ca6..6c1a813 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -46,10 +46,21 @@
 namespace impl {
 
 using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
 using aidl::android::hardware::power::Mode;
 using aidl::android::hardware::power::SessionHint;
 using aidl::android::hardware::power::SessionTag;
 using aidl::android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::WorkDurationFixedV1;
+
+using aidl::android::hardware::common::fmq::MQDescriptor;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using aidl::android::hardware::power::ChannelMessage;
+using android::hardware::EventFlag;
+
+using ChannelMessageContents = ChannelMessage::ChannelMessageContents;
+using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>;
+using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
 
 PowerAdvisor::~PowerAdvisor() = default;
 
@@ -140,15 +151,7 @@
     if (!mBootFinished.load()) {
         return;
     }
-    if (usePowerHintSession()) {
-        std::lock_guard lock(mHintSessionMutex);
-        if (ensurePowerHintSessionRunning()) {
-            auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
-            if (!ret.isOk()) {
-                mHintSession = nullptr;
-            }
-        }
-    }
+    sendHintSessionHint(SessionHint::CPU_LOAD_UP);
 }
 
 void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -160,15 +163,7 @@
 
     if (mSendUpdateImminent.exchange(false)) {
         ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
-        if (usePowerHintSession()) {
-            std::lock_guard lock(mHintSessionMutex);
-            if (ensurePowerHintSessionRunning()) {
-                auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
-                if (!ret.isOk()) {
-                    mHintSession = nullptr;
-                }
-            }
-        }
+        sendHintSessionHint(SessionHint::CPU_LOAD_RESET);
 
         if (!mHasDisplayUpdateImminent) {
             ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
@@ -210,6 +205,30 @@
             FlagManager::getInstance().adpf_use_fmq_channel();
 }
 
+void PowerAdvisor::sendHintSessionHint(SessionHint hint) {
+    if (!mBootFinished || !usePowerHintSession()) {
+        ALOGV("Power hint session is not enabled, skip sending session hint");
+        return;
+    }
+    ATRACE_CALL();
+    if (sTraceHintSessionData) ATRACE_INT("Session hint", static_cast<int>(hint));
+    {
+        std::scoped_lock lock(mHintSessionMutex);
+        if (!ensurePowerHintSessionRunning()) {
+            ALOGV("Hint session not running and could not be started, skip sending session hint");
+            return;
+        }
+        ALOGV("Sending session hint: %d", static_cast<int>(hint));
+        if (!writeHintSessionMessage<ChannelMessageContents::Tag::hint>(&hint, 1)) {
+            auto ret = mHintSession->sendHint(hint);
+            if (!ret.isOk()) {
+                ALOGW("Failed to send session hint with error: %s", ret.errorMessage());
+                mHintSession = nullptr;
+            }
+        }
+    }
+}
+
 bool PowerAdvisor::ensurePowerHintSessionRunning() {
     if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
         if (shouldCreateSessionWithConfig()) {
@@ -221,6 +240,9 @@
                                                                  &mSessionConfig);
             if (ret.isOk()) {
                 mHintSession = ret.value();
+                if (FlagManager::getInstance().adpf_use_fmq_channel_fixed()) {
+                    setUpFmq();
+                }
             }
             // If it fails the first time we try, or ever returns unsupported, assume unsupported
             else if (mFirstConfigSupportCheck || ret.isUnsupported()) {
@@ -241,9 +263,36 @@
     return mHintSession != nullptr;
 }
 
+void PowerAdvisor::setUpFmq() {
+    auto&& channelRet = getPowerHal().getSessionChannel(getpid(), static_cast<int32_t>(getuid()));
+    if (!channelRet.isOk()) {
+        ALOGE("Failed to get session channel with error: %s", channelRet.errorMessage());
+        return;
+    }
+    auto& channelConfig = channelRet.value();
+    mMsgQueue = std::make_unique<MsgQueue>(std::move(channelConfig.channelDescriptor), true);
+    LOG_ALWAYS_FATAL_IF(!mMsgQueue->isValid(), "Failed to set up hint session msg queue");
+    LOG_ALWAYS_FATAL_IF(channelConfig.writeFlagBitmask <= 0,
+                        "Invalid flag bit masks found in channel config: writeBitMask(%d)",
+                        channelConfig.writeFlagBitmask);
+    mFmqWriteMask = static_cast<uint32_t>(channelConfig.writeFlagBitmask);
+    if (!channelConfig.eventFlagDescriptor.has_value()) {
+        // For FMQ v1 in Android 15 we will force using shared event flag since the default
+        // no-op FMQ impl in Power HAL v5 will always return a valid channel config with
+        // non-zero masks but no shared flag.
+        mMsgQueue = nullptr;
+        ALOGE("No event flag descriptor found in channel config");
+        return;
+    }
+    mFlagQueue = std::make_unique<FlagQueue>(std::move(*channelConfig.eventFlagDescriptor), true);
+    LOG_ALWAYS_FATAL_IF(!mFlagQueue->isValid(), "Failed to set up hint session flag queue");
+    auto status = EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), &mEventFlag);
+    LOG_ALWAYS_FATAL_IF(status != OK, "Failed to set up hint session event flag");
+}
+
 void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
     if (!mBootFinished || !usePowerHintSession()) {
-        ALOGV("Power hint session target duration cannot be set, skipping");
+        ALOGV("Power hint session is not enabled, skipping target update");
         return;
     }
     ATRACE_CALL();
@@ -251,10 +300,15 @@
         mTargetDuration = targetDuration;
         if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
         if (targetDuration == mLastTargetDurationSent) return;
-        std::lock_guard lock(mHintSessionMutex);
-        if (ensurePowerHintSessionRunning()) {
-            ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
-            mLastTargetDurationSent = targetDuration;
+        std::scoped_lock lock(mHintSessionMutex);
+        if (!ensurePowerHintSessionRunning()) {
+            ALOGV("Hint session not running and could not be started, skip updating target");
+            return;
+        }
+        ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
+        mLastTargetDurationSent = targetDuration;
+        auto target = targetDuration.ns();
+        if (!writeHintSessionMessage<ChannelMessageContents::Tag::targetDuration>(&target, 1)) {
             auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
             if (!ret.isOk()) {
                 ALOGW("Failed to set power hint target work duration with error: %s",
@@ -302,23 +356,73 @@
     }
 
     {
-        std::lock_guard lock(mHintSessionMutex);
+        std::scoped_lock lock(mHintSessionMutex);
         if (!ensurePowerHintSessionRunning()) {
-            ALOGV("Hint session not running and could not be started, skipping");
+            ALOGV("Hint session not running and could not be started, skip reporting durations");
             return;
         }
         mHintSessionQueue.push_back(*actualDuration);
-
-        auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
-        if (!ret.isOk()) {
-            ALOGW("Failed to report actual work durations with error: %s", ret.errorMessage());
-            mHintSession = nullptr;
-            return;
+        if (!writeHintSessionMessage<
+                    ChannelMessageContents::Tag::workDuration>(mHintSessionQueue.data(),
+                                                               mHintSessionQueue.size())) {
+            auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
+            if (!ret.isOk()) {
+                ALOGW("Failed to report actual work durations with error: %s", ret.errorMessage());
+                mHintSession = nullptr;
+                return;
+            }
         }
     }
     mHintSessionQueue.clear();
 }
 
+template <ChannelMessage::ChannelMessageContents::Tag T, class In>
+bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) {
+    if (!mMsgQueue) {
+        ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T);
+        return false;
+    }
+    auto availableSize = mMsgQueue->availableToWrite();
+    if (availableSize < count) {
+        ALOGW("Skip using FMQ with message tag %hhd as there isn't enough space", T);
+        return false;
+    }
+    MsgQueue::MemTransaction tx;
+    if (!mMsgQueue->beginWrite(count, &tx)) {
+        ALOGW("Failed to begin writing message with tag %hhd", T);
+        return false;
+    }
+    for (size_t i = 0; i < count; ++i) {
+        if constexpr (T == ChannelMessageContents::Tag::workDuration) {
+            const WorkDuration& duration = contents[i];
+            new (tx.getSlot(i)) ChannelMessage{
+                    .sessionID = static_cast<int32_t>(mSessionConfig.id),
+                    .timeStampNanos =
+                            (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos,
+                    .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration,
+                                                         WorkDurationFixedV1>({
+                            .durationNanos = duration.durationNanos,
+                            .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
+                            .cpuDurationNanos = duration.cpuDurationNanos,
+                            .gpuDurationNanos = duration.gpuDurationNanos,
+                    }),
+            };
+        } else {
+            new (tx.getSlot(i)) ChannelMessage{
+                    .sessionID = static_cast<int32_t>(mSessionConfig.id),
+                    .timeStampNanos = ::android::uptimeNanos(),
+                    .data = ChannelMessageContents::make<T, In>(std::move(contents[i])),
+            };
+        }
+    }
+    if (!mMsgQueue->commitWrite(count)) {
+        ALOGW("Failed to send message with tag %hhd, fall back to binder call", T);
+        return false;
+    }
+    mEventFlag->wake(mFmqWriteMask);
+    return true;
+}
+
 void PowerAdvisor::enablePowerHintSession(bool enabled) {
     mHintSessionEnabled = enabled;
 }
@@ -334,12 +438,14 @@
     }
     LOG_ALWAYS_FATAL_IF(mHintSessionThreadIds.empty(),
                         "No thread IDs provided to power hint session!");
-    std::lock_guard lock(mHintSessionMutex);
-    if (mHintSession != nullptr) {
-        ALOGE("Cannot start power hint session: already running");
-        return false;
+    {
+        std::scoped_lock lock(mHintSessionMutex);
+        if (mHintSession != nullptr) {
+            ALOGE("Cannot start power hint session: already running");
+            return false;
+        }
+        return ensurePowerHintSessionRunning();
     }
-    return ensurePowerHintSessionRunning();
 }
 
 bool PowerAdvisor::supportsGpuReporting() {
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 161ca63..bc4a41b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -29,6 +29,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #include <aidl/android/hardware/power/IPower.h>
+#include <fmq/AidlMessageQueue.h>
 #include <powermanager/PowerHalController.h>
 #pragma clang diagnostic pop
 
@@ -237,6 +238,7 @@
     bool shouldCreateSessionWithConfig() REQUIRES(mHintSessionMutex);
 
     bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
+    void setUpFmq() REQUIRES(mHintSessionMutex);
     std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
     // Current frame's delay
     Duration mFrameDelayDuration{0ns};
@@ -272,6 +274,15 @@
     bool mHasDisplayUpdateImminent = true;
     // Queue of actual durations saved to report
     std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue;
+    std::unique_ptr<::android::AidlMessageQueue<
+            aidl::android::hardware::power::ChannelMessage,
+            ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
+            mMsgQueue GUARDED_BY(mHintSessionMutex);
+    std::unique_ptr<::android::AidlMessageQueue<
+            int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
+            mFlagQueue GUARDED_BY(mHintSessionMutex);
+    android::hardware::EventFlag* mEventFlag;
+    uint32_t mFmqWriteMask;
     // The latest values we have received for target and actual
     Duration mTargetDuration = kDefaultTargetDuration;
     // The list of thread ids, stored so we can restart the session from this class if needed
@@ -306,6 +317,12 @@
     // How long we expect hwc to run after the present call until it waits for the fence
     static constexpr const Duration kFenceWaitStartDelayValidated{150us};
     static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
+
+    void sendHintSessionHint(aidl::android::hardware::power::SessionHint hint);
+
+    template <aidl::android::hardware::power::ChannelMessage::ChannelMessageContents::Tag T,
+              class In>
+    bool writeHintSessionMessage(In* elements, size_t count) REQUIRES(mHintSessionMutex);
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 2ec20ad..2b4e234 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -279,14 +279,6 @@
     const Rect sampledBounds = sampleRegion.bounds();
     constexpr bool kHintForSeamlessTransition = false;
 
-    SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        DisplayRenderAreaBuilder displayRenderArea(sampledBounds, sampledBounds.getSize(),
-                                                   ui::Dataspace::V0_SRGB,
-                                                   kHintForSeamlessTransition,
-                                                   true /* captureSecureLayers */, displayWeak);
-        return displayRenderArea.build();
-    });
-
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
 
     auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
@@ -381,8 +373,14 @@
     constexpr bool kIsProtected = false;
 
     if (const auto fenceResult =
-                mFlinger.captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, buffer,
-                                           kRegionSampling, kGrayscale, kIsProtected, nullptr)
+                mFlinger.captureScreenshot(SurfaceFlinger::RenderAreaBuilderVariant(
+                                                   std::in_place_type<DisplayRenderAreaBuilder>,
+                                                   sampledBounds, sampledBounds.getSize(),
+                                                   ui::Dataspace::V0_SRGB,
+                                                   kHintForSeamlessTransition,
+                                                   true /* captureSecureLayers */, displayWeak),
+                                           getLayerSnapshots, buffer, kRegionSampling, kGrayscale,
+                                           kIsProtected, nullptr)
                         .get();
         fenceResult.ok()) {
         fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 496185e..0d2e514 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7899,13 +7899,6 @@
         }
     }
 
-    RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        DisplayRenderAreaBuilder displayRenderArea(args.sourceCrop, reqSize, args.dataspace,
-                                                   args.hintForSeamlessTransition,
-                                                   args.captureSecureLayers, displayWeak);
-        return displayRenderArea.build();
-    });
-
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
         getLayerSnapshots =
@@ -7918,8 +7911,12 @@
         getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
     }
 
-    captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
-                        args.allowProtected, args.grayscale, captureListener);
+    captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+                                                 args.sourceCrop, reqSize, args.dataspace,
+                                                 args.hintForSeamlessTransition,
+                                                 args.captureSecureLayers, displayWeak),
+                        getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+                        args.grayscale, captureListener);
 }
 
 void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7955,13 +7952,6 @@
         return;
     }
 
-    RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        DisplayRenderAreaBuilder displayRenderArea(Rect(), size, args.dataspace,
-                                                   args.hintForSeamlessTransition,
-                                                   false /* captureSecureLayers */, displayWeak);
-        return displayRenderArea.build();
-    });
-
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
         getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
@@ -7982,8 +7972,12 @@
     constexpr bool kAllowProtected = false;
     constexpr bool kGrayscale = false;
 
-    captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat,
-                        kAllowProtected, kGrayscale, captureListener);
+    captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+                                                 Rect(), size, args.dataspace,
+                                                 args.hintForSeamlessTransition,
+                                                 false /* captureSecureLayers */, displayWeak),
+                        getLayerSnapshots, size, args.pixelFormat, kAllowProtected, kGrayscale,
+                        captureListener);
 }
 
 ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -8064,22 +8058,6 @@
         return;
     }
 
-    RenderAreaFuture renderAreaFuture = ftl::defer(
-            [=, this]() FTL_FAKE_GUARD(kMainThreadContext) -> std::unique_ptr<RenderArea> {
-                LayerRenderAreaBuilder layerRenderArea(crop, reqSize, dataspace,
-                                                       args.captureSecureLayers,
-                                                       args.hintForSeamlessTransition, parent,
-                                                       args.childrenOnly);
-
-                frontend::LayerSnapshot* snapshot =
-                        mLayerSnapshotBuilder.getSnapshot(parent->getSequence());
-                if (!snapshot) {
-                    ALOGW("Couldn't find layer snapshot for %d", parent->getSequence());
-                } else {
-                    layerRenderArea.setLayerInfo(snapshot);
-                }
-                return layerRenderArea.build();
-            });
     GetLayerSnapshotsFunction getLayerSnapshots;
     if (mLayerLifecycleManagerEnabled) {
         std::optional<FloatRect> parentCrop = std::nullopt;
@@ -8122,8 +8100,12 @@
         return;
     }
 
-    captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
-                        args.allowProtected, args.grayscale, captureListener);
+    captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
+                                                 reqSize, dataspace, args.captureSecureLayers,
+                                                 args.hintForSeamlessTransition, parent,
+                                                 args.childrenOnly),
+                        getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+                        args.grayscale, captureListener);
 }
 
 // Creates a Future release fence for a layer and keeps track of it in a list to
@@ -8150,7 +8132,7 @@
     return protectedLayerFound;
 }
 
-void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
+void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
                                          GetLayerSnapshotsFunction getLayerSnapshots,
                                          ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
                                          bool allowProtected, bool grayscale,
@@ -8199,21 +8181,35 @@
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          WRITEABLE);
     auto futureFence =
-            captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, texture,
+            captureScreenshot(renderAreaBuilder, getLayerSnapshots, texture,
                               false /* regionSampling */, grayscale, isProtected, captureListener);
     futureFence.get();
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
-        RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
+        RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
-    auto takeScreenshotFn = [=, this, renderAreaFuture = std::move(renderAreaFuture)]() REQUIRES(
+    auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
                                     kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
         // LayerSnapshots must be obtained from the main thread.
         auto layers = getLayerSnapshots();
+
+        if (auto* layerRenderAreaBuilder =
+                    std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) {
+            // LayerSnapshotBuilder should only be accessed from the main thread.
+            frontend::LayerSnapshot* snapshot =
+                    mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence());
+            if (!snapshot) {
+                ALOGW("Couldn't find layer snapshot for %d",
+                      layerRenderAreaBuilder->layer->getSequence());
+            } else {
+                layerRenderAreaBuilder->setLayerInfo(snapshot);
+            }
+        }
+
         if (FlagManager::getInstance().ce_fence_promise()) {
             for (auto& [layer, layerFE] : layers) {
                 attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
@@ -8221,7 +8217,10 @@
         }
 
         ScreenCaptureResults captureResults;
-        std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
+        std::unique_ptr<const RenderArea> renderArea =
+                std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); },
+                           renderAreaBuilder);
+
         if (!renderArea) {
             ALOGW("Skipping screen capture because of invalid render area.");
             if (captureListener) {
@@ -8232,8 +8231,8 @@
         }
 
         ftl::SharedFuture<FenceResult> renderFuture =
-                renderScreenImpl(renderArea, buffer, regionSampling, grayscale, isProtected,
-                                 captureResults, layers);
+                renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
+                                 isProtected, captureResults, layers);
 
         if (captureListener) {
             // Defer blocking on renderFuture back to the Binder thread.
@@ -8250,9 +8249,7 @@
     };
 
     // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number
-    // of calls on the main thread. renderAreaFuture runs on the main thread and should
-    // no longer be a future, so that it does not need to make an additional jump on the
-    // main thread whenever get() is called.
+    // of calls on the main thread.
     auto future =
             mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
 
@@ -8265,16 +8262,16 @@
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
+        std::unique_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) {
     auto layers = getLayerSnapshots();
-    return renderScreenImpl(renderArea, buffer, regionSampling, grayscale, isProtected,
+    return renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
                             captureResults, layers);
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        std::shared_ptr<const RenderArea> renderArea,
+        std::unique_ptr<const RenderArea> renderArea,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
         std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2444424..4cb5aa3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -192,6 +192,9 @@
     Always,
 };
 
+struct DisplayRenderAreaBuilder;
+struct LayerRenderAreaBuilder;
+
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 class SurfaceFlinger : public BnSurfaceComposer,
@@ -383,7 +386,7 @@
 
     using TransactionSchedule = scheduler::TransactionSchedule;
     using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
-    using RenderAreaFuture = ftl::Future<std::unique_ptr<RenderArea>>;
+    using RenderAreaBuilderVariant = std::variant<DisplayRenderAreaBuilder, LayerRenderAreaBuilder>;
     using DumpArgs = Vector<String16>;
     using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
 
@@ -891,12 +894,12 @@
     // Checks if a protected layer exists in a list of layers.
     bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
 
-    void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize,
-                             ui::PixelFormat, bool allowProtected, bool grayscale,
-                             const sp<IScreenCaptureListener>&);
+    void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
+                             ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
+                             bool grayscale, const sp<IScreenCaptureListener>&);
 
     ftl::SharedFuture<FenceResult> captureScreenshot(
-            RenderAreaFuture, GetLayerSnapshotsFunction,
+            RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
 
@@ -904,13 +907,13 @@
     // not yet been captured, and thus cannot yet be passed in as a parameter.
     // Needed for TestableSurfaceFlinger.
     ftl::SharedFuture<FenceResult> renderScreenImpl(
-            std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
+            std::unique_ptr<const RenderArea>, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock)
             REQUIRES(kMainThreadContext);
 
     ftl::SharedFuture<FenceResult> renderScreenImpl(
-            std::shared_ptr<const RenderArea>,
+            std::unique_ptr<const RenderArea>,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, bool isProtected, ScreenCaptureResults&,
             std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) EXCLUDES(mStateLock)
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index f074b7d..45b3290 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -249,5 +249,6 @@
 /// Trunk stable readonly flags from outside SurfaceFlinger ///
 FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
                                      com::android::server::display::feature::flags)
+FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
 
 } // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 0acf754..592e774 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -51,6 +51,7 @@
     bool refresh_rate_overlay_on_external_display() const;
     bool adpf_gpu_sf() const;
     bool adpf_use_fmq_channel() const;
+    bool adpf_use_fmq_channel_fixed() const;
 
     /// Trunk stable readonly flags ///
     bool connected_display() const;
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 86ad86e..e74f643 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -45,6 +45,7 @@
 class PowerAdvisorTest : public testing::Test {
 public:
     void SetUp() override;
+    void SetUpFmq(bool usesSharedEventFlag, bool isQueueFull);
     void startPowerHintSession(bool returnValidSession = true);
     void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
     void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
@@ -64,16 +65,26 @@
         Duration postCompDuration = 0ms;
         bool frame1RequiresRenderEngine;
         bool frame2RequiresRenderEngine;
+        bool usesFmq = false;
+        bool usesSharedFmqFlag = true;
+        bool fmqFull = false;
     };
 
-    WorkDuration testGpuScenario(GpuTestConfig& config);
+    void testGpuScenario(GpuTestConfig& config, WorkDuration& ret);
 
 protected:
     TestableSurfaceFlinger mFlinger;
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
     MockPowerHalController* mMockPowerHalController;
     std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
+    std::shared_ptr<AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>> mBackendFmq;
+    std::shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mBackendFlagQueue;
+    android::hardware::EventFlag* mEventFlag;
+    uint32_t mWriteFlagBitmask = 2;
+    uint32_t mReadFlagBitmask = 1;
+    int64_t mSessionId = 123;
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, true);
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, false);
 };
 
 bool PowerAdvisorTest::sessionExists() {
@@ -95,12 +106,44 @@
                     ByMove(HalResult<int64_t>::fromStatus(ndk::ScopedAStatus::ok(), 16000))));
 }
 
+void PowerAdvisorTest::SetUpFmq(bool usesSharedEventFlag, bool isQueueFull) {
+    mBackendFmq = std::make_shared<
+            AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>>(2, !usesSharedEventFlag);
+    ChannelConfig config;
+    config.channelDescriptor = mBackendFmq->dupeDesc();
+    if (usesSharedEventFlag) {
+        mBackendFlagQueue =
+                std::make_shared<AidlMessageQueue<int8_t, SynchronizedReadWrite>>(1, true);
+        config.eventFlagDescriptor = mBackendFlagQueue->dupeDesc();
+        ASSERT_EQ(android::hardware::EventFlag::createEventFlag(mBackendFlagQueue
+                                                                        ->getEventFlagWord(),
+                                                                &mEventFlag),
+                  android::NO_ERROR);
+    } else {
+        ASSERT_EQ(android::hardware::EventFlag::createEventFlag(mBackendFmq->getEventFlagWord(),
+                                                                &mEventFlag),
+                  android::NO_ERROR);
+    }
+    config.writeFlagBitmask = static_cast<int32_t>(mWriteFlagBitmask);
+    config.readFlagBitmask = static_cast<int32_t>(mReadFlagBitmask);
+    ON_CALL(*mMockPowerHalController, getSessionChannel)
+            .WillByDefault(Return(
+                    ByMove(HalResult<ChannelConfig>::fromStatus(Status::ok(), std::move(config)))));
+    startPowerHintSession();
+    if (isQueueFull) {
+        std::vector<ChannelMessage> msgs;
+        msgs.resize(2);
+        mBackendFmq->writeBlocking(msgs.data(), 2, mReadFlagBitmask, mWriteFlagBitmask,
+                                   std::chrono::nanoseconds(1ms).count(), mEventFlag);
+    }
+}
+
 void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
     mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
     if (returnValidSession) {
         ON_CALL(*mMockPowerHalController, createHintSessionWithConfig)
                 .WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{
-                                             .id = 12}),
+                                             .id = mSessionId}),
                                      Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
                                                     fromStatus(binder::Status::ok(),
                                                                mMockPowerHintSession))));
@@ -137,11 +180,17 @@
     mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true);
 }
 
-WorkDuration PowerAdvisorTest::testGpuScenario(GpuTestConfig& config) {
+void PowerAdvisorTest::testGpuScenario(GpuTestConfig& config, WorkDuration& ret) {
     SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_gpu_sf,
                       config.adpfGpuFlagOn);
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, config.usesFmq);
     mPowerAdvisor->onBootFinished();
-    startPowerHintSession();
+    bool expectsFmqSuccess = config.usesSharedFmqFlag && !config.fmqFull;
+    if (config.usesFmq) {
+        SetUpFmq(config.usesSharedFmqFlag, config.fmqFull);
+    } else {
+        startPowerHintSession();
+    }
 
     std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
                                       GpuVirtualDisplayId(1)};
@@ -150,8 +199,41 @@
     // 60hz
 
     TimePoint startTime = TimePoint::now();
+    int64_t target;
+    SessionHint hint;
+    if (!config.usesFmq || !expectsFmqSuccess) {
+        EXPECT_CALL(*mMockPowerHintSession, updateTargetWorkDuration(_))
+                .Times(1)
+                .WillOnce(DoAll(testing::SaveArg<0>(&target),
+                                testing::Return(testing::ByMove(HalResult<void>::ok()))));
+        EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+                .Times(1)
+                .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+                                testing::Return(testing::ByMove(HalResult<void>::ok()))));
+    }
     // advisor only starts on frame 2 so do an initial frame
     fakeBasicFrameTiming(startTime, config.vsyncPeriod);
+    // send a load hint
+    mPowerAdvisor->notifyCpuLoadUp();
+    if (config.usesFmq && expectsFmqSuccess) {
+        std::vector<ChannelMessage> msgs;
+        ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+        msgs.resize(2);
+        ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 2, mReadFlagBitmask, mWriteFlagBitmask,
+                                              std::chrono::nanoseconds(1ms).count(), mEventFlag));
+        ASSERT_EQ(msgs[0].sessionID, mSessionId);
+        ASSERT_GE(msgs[0].timeStampNanos, startTime.ns());
+        ASSERT_EQ(msgs[0].data.getTag(),
+                  ChannelMessage::ChannelMessageContents::Tag::targetDuration);
+        target = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::targetDuration>();
+        ASSERT_EQ(msgs[1].sessionID, mSessionId);
+        ASSERT_GE(msgs[1].timeStampNanos, startTime.ns());
+        ASSERT_EQ(msgs[1].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::hint);
+        hint = msgs[1].data.get<ChannelMessage::ChannelMessageContents::Tag::hint>();
+    }
+    ASSERT_EQ(target, config.vsyncPeriod.ns());
+    ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+
     setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod);
 
     // report GPU
@@ -171,6 +253,10 @@
     std::this_thread::sleep_for(config.vsyncPeriod);
     startTime = TimePoint::now();
     fakeBasicFrameTiming(startTime, config.vsyncPeriod);
+    if (config.usesFmq && expectsFmqSuccess) {
+        // same target update will not trigger FMQ write
+        ASSERT_EQ(mBackendFmq->availableToRead(), 0uL);
+    }
     setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod);
 
     // report GPU
@@ -192,14 +278,31 @@
     mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime, startTime);
     mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime, startTime);
 
-    std::vector<aidl::android::hardware::power::WorkDuration> durationReq;
-    EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(_))
-            .Times(1)
-            .WillOnce(DoAll(testing::SaveArg<0>(&durationReq),
-                            testing::Return(testing::ByMove(HalResult<void>::ok()))));
-    mPowerAdvisor->reportActualWorkDuration();
-    EXPECT_EQ(durationReq.size(), 1u);
-    return durationReq[0];
+    if (config.usesFmq && expectsFmqSuccess) {
+        mPowerAdvisor->reportActualWorkDuration();
+        ASSERT_EQ(mBackendFmq->availableToRead(), 1uL);
+        std::vector<ChannelMessage> msgs;
+        msgs.resize(1);
+        ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+                                              std::chrono::nanoseconds(1ms).count(), mEventFlag));
+        ASSERT_EQ(msgs[0].sessionID, mSessionId);
+        ASSERT_GE(msgs[0].timeStampNanos, startTime.ns());
+        ASSERT_EQ(msgs[0].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::workDuration);
+        auto actual = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::workDuration>();
+        ret.workPeriodStartTimestampNanos = actual.workPeriodStartTimestampNanos;
+        ret.cpuDurationNanos = actual.cpuDurationNanos;
+        ret.gpuDurationNanos = actual.gpuDurationNanos;
+        ret.durationNanos = actual.durationNanos;
+    } else {
+        std::vector<aidl::android::hardware::power::WorkDuration> durationReq;
+        EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(_))
+                .Times(1)
+                .WillOnce(DoAll(testing::SaveArg<0>(&durationReq),
+                                testing::Return(testing::ByMove(HalResult<void>::ok()))));
+        mPowerAdvisor->reportActualWorkDuration();
+        ASSERT_EQ(durationReq.size(), 1u);
+        ret = std::move(durationReq[0]);
+    }
 }
 
 Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
@@ -375,13 +478,6 @@
     mPowerAdvisor->reportActualWorkDuration();
 }
 
-TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
-    EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
-    mPowerAdvisor->onBootFinished();
-    startPowerHintSession();
-    mPowerAdvisor->startPowerHintSession({1, 2, 3});
-}
-
 TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) {
     // notifyDisplayUpdateImminentAndCpuReset or notifyCpuLoadUp gets called in background
     // reportActual gets called during callback and sees true session, passes ensure
@@ -464,15 +560,21 @@
 }
 
 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, createHintSessionWithConfig)
+            .Times(1)
+            .WillOnce(Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+                                     fromStatus(ndk::ScopedAStatus::fromExceptionCode(
+                                                        EX_UNSUPPORTED_OPERATION),
+                                                nullptr)));
+
     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});
+    ASSERT_TRUE(mPowerAdvisor->startPowerHintSession({1, 2, 3}));
 }
 
 TEST_F(PowerAdvisorTest, setGpuFenceTime_cpuThenGpuFrames) {
@@ -487,7 +589,8 @@
             .frame1RequiresRenderEngine = false,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, 0L);
     EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
@@ -505,7 +608,8 @@
             .frame1RequiresRenderEngine = false,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
     EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
     EXPECT_EQ(res.durationNanos, toNanos(30ms + getErrorMargin()));
@@ -523,7 +627,8 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = false,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, 0L);
     EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin()));
@@ -540,7 +645,8 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = false,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
     EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin()));
@@ -559,7 +665,8 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, 0L);
     EXPECT_GE(res.durationNanos, toNanos(50ms + getErrorMargin()));
@@ -577,7 +684,8 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, toNanos(50ms));
     EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
     EXPECT_EQ(res.durationNanos, toNanos(50ms + getErrorMargin()));
@@ -594,7 +702,8 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, 0L);
     EXPECT_EQ(res.cpuDurationNanos, 0L);
     EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
@@ -612,11 +721,122 @@
             .frame1RequiresRenderEngine = true,
             .frame2RequiresRenderEngine = true,
     };
-    WorkDuration res = testGpuScenario(config);
+    WorkDuration res;
+    testGpuScenario(config, res);
     EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
     EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
     EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
 }
 
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration) {
+    GpuTestConfig config{
+            .adpfGpuFlagOn = true,
+            .frame1GpuFenceDuration = 30ms,
+            .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+            .vsyncPeriod = 10ms,
+            .presentDuration = 22ms,
+            .postCompDuration = 88ms,
+            .frame1RequiresRenderEngine = true,
+            .frame2RequiresRenderEngine = true,
+            .usesFmq = true,
+            .usesSharedFmqFlag = true,
+    };
+    WorkDuration res;
+    testGpuScenario(config, res);
+    EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+    EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+    EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration_noSharedFlag) {
+    GpuTestConfig config{
+            .adpfGpuFlagOn = true,
+            .frame1GpuFenceDuration = 30ms,
+            .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+            .vsyncPeriod = 10ms,
+            .presentDuration = 22ms,
+            .postCompDuration = 88ms,
+            .frame1RequiresRenderEngine = true,
+            .frame2RequiresRenderEngine = true,
+            .usesFmq = true,
+            .usesSharedFmqFlag = false,
+    };
+    WorkDuration res;
+    testGpuScenario(config, res);
+    EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+    EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+    EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration_queueFull) {
+    GpuTestConfig config{.adpfGpuFlagOn = true,
+                         .frame1GpuFenceDuration = 30ms,
+                         .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+                         .vsyncPeriod = 10ms,
+                         .presentDuration = 22ms,
+                         .postCompDuration = 88ms,
+                         .frame1RequiresRenderEngine = true,
+                         .frame2RequiresRenderEngine = true,
+                         .usesFmq = true,
+                         .usesSharedFmqFlag = true,
+                         .fmqFull = true};
+    WorkDuration res;
+    testGpuScenario(config, res);
+    EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+    EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+    EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint) {
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    mPowerAdvisor->onBootFinished();
+    SetUpFmq(true, false);
+    auto startTime = uptimeNanos();
+    mPowerAdvisor->notifyCpuLoadUp();
+    std::vector<ChannelMessage> msgs;
+    ASSERT_EQ(mBackendFmq->availableToRead(), 1uL);
+    msgs.resize(1);
+    ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+                                          std::chrono::nanoseconds(1ms).count(), mEventFlag));
+    ASSERT_EQ(msgs[0].sessionID, mSessionId);
+    ASSERT_GE(msgs[0].timeStampNanos, startTime);
+    ASSERT_EQ(msgs[0].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::hint);
+    auto hint = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::hint>();
+    ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint_noSharedFlag) {
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    mPowerAdvisor->onBootFinished();
+    SetUpFmq(false, false);
+    SessionHint hint;
+    EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+            .Times(1)
+            .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+                            testing::Return(testing::ByMove(HalResult<void>::ok()))));
+    mPowerAdvisor->notifyCpuLoadUp();
+    ASSERT_EQ(mBackendFmq->availableToRead(), 0uL);
+    ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) {
+    SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    mPowerAdvisor->onBootFinished();
+    SetUpFmq(true, true);
+    ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+    SessionHint hint;
+    EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+            .Times(1)
+            .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+                            testing::Return(testing::ByMove(HalResult<void>::ok()))));
+    std::vector<ChannelMessage> msgs;
+    msgs.resize(1);
+    mBackendFmq->writeBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+                               std::chrono::nanoseconds(1ms).count(), mEventFlag);
+    mPowerAdvisor->notifyCpuLoadUp();
+    ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+    ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+}
+
 } // namespace
 } // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a4c75a4..a0c1372 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -476,7 +476,7 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
+    auto renderScreenImpl(std::unique_ptr<const RenderArea> renderArea,
                           SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
                           const std::shared_ptr<renderengine::ExternalTexture>& buffer,
                           bool regionSampling) {