Merge tag 'android-15.0.0_r32' of https://android.googlesource.com/platform/frameworks/native into HEAD

Android 15.0.0 Release 32 (BP1A.250505.005)

Change-Id: I087087213d31ddee9befa1a0c34afa21b8df56f9
diff --git a/services/surfaceflinger/ActivePictureUpdater.cpp b/services/surfaceflinger/ActivePictureUpdater.cpp
new file mode 100644
index 0000000..210e948
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ActivePictureUpdater.h"
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "LayerFE.h"
+
+namespace android {
+
+void ActivePictureUpdater::onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+                                           const CompositionResult& result) {
+    if (result.wasPictureProfileCommitted) {
+        gui::ActivePicture picture;
+        picture.layerId = int32_t(layer.sequence);
+        picture.ownerUid = int32_t(layer.getOwnerUid());
+        // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+        if (layerFE.getCompositionState()) {
+            picture.pictureProfileId = layerFE.getCompositionState()->pictureProfileHandle.getId();
+        } else {
+            picture.pictureProfileId = result.pictureProfileHandle.getId();
+        }
+        mNewActivePictures.push_back(picture);
+    }
+}
+
+bool ActivePictureUpdater::updateAndHasChanged() {
+    bool hasChanged = true;
+    if (mNewActivePictures.size() == mOldActivePictures.size()) {
+        auto compare = [](const gui::ActivePicture& lhs, const gui::ActivePicture& rhs) -> int {
+            if (lhs.layerId == rhs.layerId) {
+                return lhs.pictureProfileId < rhs.pictureProfileId;
+            }
+            return lhs.layerId < rhs.layerId;
+        };
+        std::sort(mNewActivePictures.begin(), mNewActivePictures.end(), compare);
+        if (std::equal(mNewActivePictures.begin(), mNewActivePictures.end(),
+                       mOldActivePictures.begin())) {
+            hasChanged = false;
+        }
+    }
+    std::swap(mOldActivePictures, mNewActivePictures);
+    mNewActivePictures.resize(0);
+    return hasChanged;
+}
+
+const std::vector<gui::ActivePicture>& ActivePictureUpdater::getActivePictures() const {
+    return mOldActivePictures;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ActivePictureUpdater.h b/services/surfaceflinger/ActivePictureUpdater.h
new file mode 100644
index 0000000..20779bb
--- /dev/null
+++ b/services/surfaceflinger/ActivePictureUpdater.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <android/gui/ActivePicture.h>
+
+namespace android {
+
+class Layer;
+class LayerFE;
+struct CompositionResult;
+
+// Keeps track of active pictures - layers that are undergoing picture processing.
+class ActivePictureUpdater {
+public:
+    // Called for each visible layer when SurfaceFlinger finishes composing.
+    void onLayerComposed(const Layer& layer, const LayerFE& layerFE,
+                         const CompositionResult& result);
+
+    // Update internals and return whether the set of active pictures have changed.
+    bool updateAndHasChanged();
+
+    // The current set of active pictures.
+    const std::vector<gui::ActivePicture>& getActivePictures() const;
+
+private:
+    std::vector<gui::ActivePicture> mOldActivePictures;
+    std::vector<gui::ActivePicture> mNewActivePictures;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index da57b68..92fae1e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -83,6 +83,7 @@
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog_surfaceflinger",
         "libsync",
         "libui",
         "libutils",
@@ -148,6 +149,46 @@
     },
 }
 
+// libsurfaceflinger_backend_{headers|sources} are a step towards pulling out
+// the "backend" sources to clean up the dependency graph between
+// CompositionEngine and SurfaceFlinger. Completing the cleanup would require
+// moving the headers in particular so that the dependency can strictly be a
+// DAG. There would certainly be additional cleanups: VirtualDisplaySurface.cpp
+// and FrameBufferSurface.cpp likely belong in CompositionEngine for example.
+cc_library_headers {
+    name: "libsurfaceflinger_backend_headers",
+    export_include_dirs: ["."],
+    static_libs: ["libserviceutils"],
+    export_static_lib_headers: ["libserviceutils"],
+
+    shared_libs: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "libbinder_ndk",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.configstore-utils",
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "libbinder_ndk",
+    ],
+}
+
+filegroup {
+    name: "libsurfaceflinger_backend_sources",
+    srcs: [
+        "PowerAdvisor/*.cpp",
+        "DisplayHardware/AidlComposerHal.cpp",
+        "DisplayHardware/ComposerHal.cpp",
+        "DisplayHardware/FramebufferSurface.cpp",
+        "DisplayHardware/HWC2.cpp",
+        "DisplayHardware/HWComposer.cpp",
+        "DisplayHardware/HidlComposerHal.cpp",
+        "DisplayHardware/VirtualDisplaySurface.cpp",
+    ],
+}
+
 cc_library_headers {
     name: "libsurfaceflinger_headers",
     export_include_dirs: ["."],
@@ -158,23 +199,16 @@
 filegroup {
     name: "libsurfaceflinger_sources",
     srcs: [
+        ":libsurfaceflinger_backend_sources",
+        "ActivePictureUpdater.cpp",
         "BackgroundExecutor.cpp",
         "Client.cpp",
         "ClientCache.cpp",
         "Display/DisplayModeController.cpp",
         "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
-        "DisplayHardware/AidlComposerHal.cpp",
-        "DisplayHardware/ComposerHal.cpp",
-        "DisplayHardware/FramebufferSurface.cpp",
-        "DisplayHardware/HWC2.cpp",
-        "DisplayHardware/HWComposer.cpp",
-        "DisplayHardware/HidlComposerHal.cpp",
-        "DisplayHardware/PowerAdvisor.cpp",
-        "DisplayHardware/VirtualDisplaySurface.cpp",
         "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
-        "EventLog/EventLog.cpp",
         "FrontEnd/LayerCreationArgs.cpp",
         "FrontEnd/LayerHandle.cpp",
         "FrontEnd/LayerSnapshot.cpp",
@@ -252,7 +286,6 @@
     ],
     static_libs: [
         "android.frameworks.displayservice@1.0",
-        "libc++fs",
         "libdisplayservicehidl",
         "libserviceutils",
     ],
@@ -280,7 +313,7 @@
         "libSurfaceFlingerProp",
     ],
 
-    logtags: ["EventLog/EventLogTags.logtags"],
+    logtags: ["surfaceflinger.logtags"],
 }
 
 subdirs = [
@@ -315,3 +348,37 @@
         "libSurfaceFlingerProperties",
     ],
 }
+
+cc_library {
+    name: "libstatslog_surfaceflinger",
+    generated_sources: ["statslog_surfaceflinger.cpp"],
+    generated_headers: ["statslog_surfaceflinger.h"],
+    export_generated_headers: ["statslog_surfaceflinger.h"],
+    shared_libs: [
+        "libbinder",
+        "libstatsbootstrap",
+        "libutils",
+        "android.os.statsbootstrap_aidl-cpp",
+    ],
+}
+
+genrule {
+    name: "statslog_surfaceflinger.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_surfaceflinger.h" +
+        " --module surfaceflinger --namespace android,surfaceflinger,stats --bootstrap",
+    out: [
+        "statslog_surfaceflinger.h",
+    ],
+}
+
+genrule {
+    name: "statslog_surfaceflinger.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_surfaceflinger.cpp" +
+        " --module surfaceflinger --namespace android,surfaceflinger,stats" +
+        " --importHeader statslog_surfaceflinger.h --bootstrap",
+    out: [
+        "statslog_surfaceflinger.cpp",
+    ],
+}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 5b1aacd..2c3e50c 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -42,6 +42,7 @@
         "libutils",
     ],
     static_libs: [
+        "libguiflags",
         "libmath",
         "librenderengine",
         "libtimestats",
@@ -49,9 +50,7 @@
         "libaidlcommonsupport",
         "libprocessgroup",
         "libprocessgroup_util",
-        "libcgrouprc",
         "libjsoncpp",
-        "libcgrouprc_format",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -59,7 +58,7 @@
         "android.hardware.graphics.composer@2.3-command-buffer",
         "android.hardware.graphics.composer@2.4-command-buffer",
         "android.hardware.graphics.composer3-command-buffer",
-        "libsurfaceflinger_headers",
+        "libsurfaceflinger_backend_headers",
     ],
 }
 
@@ -143,6 +142,8 @@
     ],
     srcs: [
         ":libcompositionengine_sources",
+        ":libsurfaceflinger_backend_mock_sources",
+        ":libsurfaceflinger_backend_sources",
         "tests/planner/CachedSetTest.cpp",
         "tests/planner/FlattenerTest.cpp",
         "tests/planner/LayerStateTest.cpp",
@@ -153,14 +154,14 @@
         "tests/DisplayTest.cpp",
         "tests/HwcAsyncWorkerTest.cpp",
         "tests/HwcBufferCacheTest.cpp",
-        "tests/MockHWC2.cpp",
-        "tests/MockHWComposer.cpp",
-        "tests/MockPowerAdvisor.cpp",
         "tests/OutputLayerTest.cpp",
         "tests/OutputTest.cpp",
         "tests/ProjectionSpaceTest.cpp",
         "tests/RenderSurfaceTest.cpp",
     ],
+    header_libs: [
+        "libsurfaceflinger_backend_mock_headers",
+    ],
     static_libs: [
         "libcompositionengine_mocks",
         "libgui_mocks",
@@ -169,6 +170,7 @@
         "libgtest",
     ],
     shared_libs: [
+        "libbinder_ndk",
         // For some reason, libvulkan isn't picked up from librenderengine
         // Probably ASAN related?
         "libvulkan",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 6e60839..252adaa 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -24,7 +24,7 @@
 #include <ui/Size.h>
 #include <ui/StaticDisplayInfo.h>
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -46,9 +46,15 @@
     // content.
     bool isProtected = false;
 
+    // True if this display has picture processing hardware and pipelines.
+    bool hasPictureProcessing = false;
+
+    // The number of layer-specific picture-processing pipelines.
+    int32_t maxLayerPictureProfiles = 0;
+
     // Optional pointer to the power advisor interface, if one is needed for
     // this display.
-    Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+    adpf::PowerAdvisor* powerAdvisor = nullptr;
 
     // Debugging. Human readable name for the display.
     std::string name;
@@ -82,7 +88,17 @@
         return *this;
     }
 
-    DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
+    DisplayCreationArgsBuilder& setHasPictureProcessing(bool hasPictureProcessing) {
+        mArgs.hasPictureProcessing = hasPictureProcessing;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setMaxLayerPictureProfiles(int32_t maxLayerPictureProfiles) {
+        mArgs.maxLayerPictureProfiles = maxLayerPictureProfiles;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) {
         mArgs.powerAdvisor = powerAdvisor;
         return *this;
     }
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 4e080b3..cda4edc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -148,9 +148,6 @@
     virtual std::optional<LayerSettings> prepareClientComposition(
             ClientCompositionTargetSettings&) const = 0;
 
-    // Called after the layer is displayed to update the presentation fence
-    virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
-
     // Initializes a promise for a buffer release fence and provides the future for that
     // fence. This should only be called when a promise has not yet been created, or
     // after the previous promise has already been fulfilled. Attempting to call this
@@ -164,6 +161,9 @@
     // Checks if the buffer's release fence has been set
     virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
 
+    // Indicates that the picture profile request was applied to this layer.
+    virtual void onPictureProfileCommitted() = 0;
+
     // Gets some kind of identifier for the layer for debug purposes.
     virtual const char* getDebugName() const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index d1429a2..fb8fed0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -19,11 +19,13 @@
 #include <cstdint>
 
 #include <android/gui/CachingHint.h>
+#include <gui/DisplayLuts.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/ShadowSettings.h>
@@ -155,7 +157,7 @@
     uint32_t geomBufferTransform{0};
     Rect geomBufferSize;
     Rect geomContentCrop;
-    Rect geomCrop;
+    FloatRect geomCrop;
 
     GenericLayerMetadataMap metadata;
 
@@ -218,7 +220,18 @@
     float currentHdrSdrRatio = 1.f;
     float desiredHdrSdrRatio = 1.f;
 
+    // A picture profile handle refers to a PictureProfile configured on the display, which is a
+    // set of parameters that configures the picture processing hardware that is used to enhance
+    // the quality of buffer contents.
+    PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE};
+
+    // A layer's priority in terms of limited picture processing pipeline utilization.
+    int64_t pictureProfilePriority;
+
     gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+
+    std::shared_ptr<gui::DisplayLuts> luts;
+
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 191d475..bda7856 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <ftl/future.h>
+#include <ftl/optional.h>
 #include <cstdint>
 #include <iterator>
 #include <optional>
@@ -26,18 +28,18 @@
 #include <vector>
 
 #include <compositionengine/LayerFE.h>
-#include <ftl/future.h>
 #include <renderengine/LayerSettings.h>
+#include <ui/DisplayIdentification.h>
 #include <ui/Fence.h>
 #include <ui/FenceTime.h>
 #include <ui/GraphicTypes.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
-#include <ui/DisplayIdentification.h>
 #include "DisplayHardware/HWComposer.h"
 
 namespace android {
@@ -167,7 +169,7 @@
     virtual bool isValid() const = 0;
 
     // Returns the DisplayId the output represents, if it has one
-    virtual std::optional<DisplayId> getDisplayId() const = 0;
+    virtual ftl::Optional<DisplayId> getDisplayId() const = 0;
 
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
@@ -329,6 +331,11 @@
     virtual bool isPowerHintSessionGpuReportingEnabled() = 0;
     virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
     virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
+    virtual const aidl::android::hardware::graphics::composer3::OverlayProperties*
+    getOverlaySupport() = 0;
+    virtual bool hasPictureProcessing() const = 0;
+    virtual int32_t getMaxLayerPictureProfiles() const = 0;
+    virtual void applyPictureProfile() = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 4dbf8d2..2e7a7d9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <ui/PictureProfileHandle.h>
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 
@@ -86,6 +87,16 @@
     // longer cares about.
     virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
 
+    // Get the relative priority of the layer's picture profile with respect to the importance of
+    // the visual content to the user experience. Lower is higher priority.
+    virtual int64_t getPictureProfilePriority() const = 0;
+
+    // The picture profile handle for the layer.
+    virtual const PictureProfileHandle& getPictureProfileHandle() const = 0;
+
+    // Commit the picture profile to the composition state.
+    virtual void commitPictureProfileToCompositionState() = 0;
+
     // Recalculates the state of the output layer from the output-independent
     // layer. If includeGeometry is false, the geometry state can be skipped.
     // internalDisplayRotationFlags must be set to the rotation flags for the
@@ -93,7 +104,10 @@
     // transform, if needed.
     virtual void updateCompositionState(
             bool includeGeometry, bool forceClientComposition,
-            ui::Transform::RotationFlags internalDisplayRotationFlags) = 0;
+            ui::Transform::RotationFlags internalDisplayRotationFlags,
+            const std::optional<std::vector<
+                    std::optional<aidl::android::hardware::graphics::composer3::LutProperties>>>
+                    properties) = 0;
 
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
@@ -128,6 +142,12 @@
     // Applies a HWC device layer request
     virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0;
 
+    // Applies a HWC device layer lut
+    virtual void applyDeviceLayerLut(
+            ndk::ScopedFileDescriptor,
+            std::vector<std::pair<
+                    int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0;
+
     // Returns true if the composition settings scale pixels
     virtual bool needsFiltering() const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index d1eff24..5519aaf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -30,7 +30,7 @@
 #include <ui/DisplayIdentification.h>
 
 #include "DisplayHardware/HWComposer.h"
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -45,7 +45,7 @@
     virtual ~Display();
 
     // compositionengine::Output overrides
-    std::optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayId> getDisplayId() const override;
     bool isValid() const override;
     void dump(std::string&) const override;
     using compositionengine::impl::Output::setReleasedLayers;
@@ -82,11 +82,13 @@
     using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
     using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
     using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
+    using LayerLuts = android::HWComposer::DeviceRequestedChanges::LayerLuts;
     virtual bool allLayersRequireClientComposition() const;
     virtual void applyChangedTypesToLayers(const ChangedTypes&);
     virtual void applyDisplayRequests(const DisplayRequests&);
     virtual void applyLayerRequestsToLayers(const LayerRequests&);
     virtual void applyClientTargetRequests(const ClientTargetProperty&);
+    virtual void applyLayerLutsToLayers(const LayerLuts&);
 
     // Internal
     virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
@@ -98,9 +100,16 @@
     void setHintSessionGpuStart(TimePoint startTime) override;
     void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
     void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
+    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
+            override;
+    bool hasPictureProcessing() const override;
+    int32_t getMaxLayerPictureProfiles() const override;
+
     DisplayId mId;
     bool mIsDisconnected = false;
-    Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::PowerAdvisor* mPowerAdvisor = nullptr;
+    bool mHasPictureProcessing = false;
+    int32_t mMaxLayerPictureProfiles = 0;
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 9990a74..0ccdd22 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,6 +16,11 @@
 
 #pragma once
 
+#include <ftl/optional.h>
+#include <memory>
+#include <utility>
+#include <vector>
+
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
@@ -28,10 +33,6 @@
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/LayerSettings.h>
 
-#include <memory>
-#include <utility>
-#include <vector>
-
 namespace android::compositionengine::impl {
 
 // The implementation class contains the common implementation, but does not
@@ -43,7 +44,7 @@
 
     // compositionengine::Output overrides
     bool isValid() const override;
-    std::optional<DisplayId> getDisplayId() const override;
+    ftl::Optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
     void setLayerCachingEnabled(bool) override;
     void setLayerCachingTexturePoolEnabled(bool) override;
@@ -84,13 +85,14 @@
     bool supportsOffloadPresent() const override { return false; }
     void offloadPresentNextFrame() override;
 
-    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
     void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
     void collectVisibleLayers(const CompositionRefreshArgs&,
                               compositionengine::Output::CoverageState&) override;
     void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
                                     compositionengine::Output::CoverageState&) override;
     void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+    void commitPictureProfilesToCompositionState();
 
     void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void planComposition() override;
@@ -151,6 +153,9 @@
     void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
     bool isPowerHintSessionEnabled() override;
     bool isPowerHintSessionGpuReportingEnabled() override;
+    bool hasPictureProcessing() const override;
+    int32_t getMaxLayerPictureProfiles() const override;
+    void applyPictureProfile() override;
     void dumpBase(std::string&) const;
 
     // Implemented by the final implementation for the final state it uses.
@@ -164,6 +169,8 @@
     bool mustRecompose() const;
 
     const std::string& getNamePlusId() const { return mNamePlusId; }
+    const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport()
+            override;
 
 private:
     void dirtyEntireOutput();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f8ffde1..c76b344 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -35,6 +35,7 @@
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/ProjectionSpace.h>
 #include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -170,6 +171,8 @@
 
     ICEPowerCallback* powerCallback = nullptr;
 
+    PictureProfileHandle pictureProfileHandle;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index f383392..712b551 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -25,12 +25,15 @@
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/OutputLayer.h>
 #include <ui/FloatRect.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 
 #include <ui/DisplayIdentification.h>
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
+using aidl::android::hardware::graphics::composer3::LutProperties;
+
 namespace android::compositionengine {
 
 struct LayerFECompositionState;
@@ -46,9 +49,14 @@
     void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
     void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+    int64_t getPictureProfilePriority() const override;
+    const PictureProfileHandle& getPictureProfileHandle() const override;
+    void commitPictureProfileToCompositionState() override;
 
     void updateCompositionState(bool includeGeometry, bool forceClientComposition,
-                                ui::Transform::RotationFlags) override;
+                                ui::Transform::RotationFlags,
+                                const std::optional<std::vector<std::optional<LutProperties>>>
+                                        properties = std::nullopt) override;
     void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
                          bool isPeekingThrough) override;
     void writeCursorPositionToHWC() const override;
@@ -60,6 +68,8 @@
             aidl::android::hardware::graphics::composer3::Composition) override;
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
+    void applyDeviceLayerLut(ndk::ScopedFileDescriptor,
+                             std::vector<std::pair<int, LutProperties>>) override;
     bool needsFiltering() const override;
     std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
 
@@ -90,10 +100,13 @@
     void writeCompositionTypeToHWC(HWC2::Layer*,
                                    aidl::android::hardware::graphics::composer3::Composition,
                                    bool isPeekingThrough, bool skipLayer);
+    void writeLutToHWC(HWC2::Layer*, const LayerFECompositionState&);
     void detectDisallowedCompositionTypeChange(
             aidl::android::hardware::graphics::composer3::Composition from,
             aidl::android::hardware::graphics::composer3::Composition to) const;
     bool isClientCompositionForced(bool isPeekingThrough) const;
+    void updateLuts(const LayerFECompositionState&,
+                    const std::optional<std::vector<std::optional<LutProperties>>>& properties);
 };
 
 // This template factory function standardizes the implementation details of the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 6c419da..c558739 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -18,9 +18,11 @@
 
 #include <compositionengine/ProjectionSpace.h>
 #include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/DisplayLuts.h>
 #include <renderengine/ExternalTexture.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicTypes.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -100,6 +102,9 @@
     // order to save power.
     Region outputSpaceBlockingRegionHint;
 
+    // The picture profile for this layer.
+    PictureProfileHandle pictureProfileHandle;
+
     // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState
     struct {
         std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
@@ -151,6 +156,9 @@
 
         // True when this layer was skipped as part of SF-side layer caching.
         bool layerSkipped = false;
+
+        // lut information
+        std::shared_ptr<gui::DisplayLuts> luts;
     };
 
     // The HWC state is optional, and is only set up if there is any potential
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 05a5d38..272fa3e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -50,9 +50,6 @@
                        std::optional<compositionengine::LayerFE::LayerSettings>(
                                compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
-    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
-                (override));
-
     MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
     MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
     MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
@@ -61,6 +58,7 @@
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
     MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
     MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
+    MOCK_METHOD0(onPictureProfileCommitted, void());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d5bf2b5..f2c265a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -34,7 +34,7 @@
     virtual ~Output();
 
     MOCK_CONST_METHOD0(isValid, bool());
-    MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
+    MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
     MOCK_METHOD1(setLayerCachingEnabled, void(bool));
@@ -140,6 +140,11 @@
     MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine));
     MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
     MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ());
+    MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*),
+                getOverlaySupport, ());
+    MOCK_METHOD(bool, hasPictureProcessing, (), (const));
+    MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (), (const));
+    MOCK_METHOD(void, applyPictureProfile, ());
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 5fef63a..9333ebb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -43,7 +43,10 @@
     MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
-    MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
+    MOCK_METHOD(void, updateCompositionState,
+                (bool, bool, ui::Transform::RotationFlags,
+                 (const std::optional<std::vector<std::optional<
+                          aidl::android::hardware::graphics::composer3::LutProperties>>>)));
     MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool));
     MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
 
@@ -56,7 +59,13 @@
     MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
     MOCK_CONST_METHOD0(needsFiltering, bool());
     MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
-
+    MOCK_METHOD(void, applyDeviceLayerLut,
+                (ndk::ScopedFileDescriptor,
+                 (std::vector<std::pair<
+                          int, aidl::android::hardware::graphics::composer3::LutProperties>>)));
+    MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const));
+    MOCK_METHOD(const PictureProfileHandle&, getPictureProfileHandle, (), (const));
+    MOCK_METHOD(void, commitPictureProfileToCompositionState, ());
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 5c5d0cd..cfcce47 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -198,25 +198,23 @@
 // these buffers and fire a NO_FENCE to release it. This ensures that all
 // promises for buffer releases are fulfilled at the end of composition.
 void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
-    if (FlagManager::getInstance().ce_fence_promise()) {
-        SFTRACE_CALL();
-        ALOGV(__FUNCTION__);
+    SFTRACE_CALL();
+    ALOGV(__FUNCTION__);
 
-        for (auto& layerFE : args.layers) {
-            if (layerFE->getReleaseFencePromiseStatus() ==
-                LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
-                layerFE->setReleaseFence(Fence::NO_FENCE);
-            }
+    for (auto& layerFE : args.layers) {
+        if (layerFE->getReleaseFencePromiseStatus() ==
+            LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+            layerFE->setReleaseFence(Fence::NO_FENCE);
         }
+    }
 
-        // List of layersWithQueuedFrames does not necessarily overlap with
-        // list of layers, so those layersWithQueuedFrames also need any
-        // unfulfilled promises to be resolved for completeness.
-        for (auto& layerFE : args.layersWithQueuedFrames) {
-            if (layerFE->getReleaseFencePromiseStatus() ==
-                LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
-                layerFE->setReleaseFence(Fence::NO_FENCE);
-            }
+    // List of layersWithQueuedFrames does not necessarily overlap with
+    // list of layers, so those layersWithQueuedFrames also need any
+    // unfulfilled promises to be resolved for completeness.
+    for (auto& layerFE : args.layersWithQueuedFrames) {
+        if (layerFE->getReleaseFencePromiseStatus() ==
+            LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+            layerFE->setReleaseFence(Fence::NO_FENCE);
         }
     }
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 77b1940..e37ce0a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -36,7 +36,7 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
@@ -54,6 +54,8 @@
 void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
     mId = args.id;
     mPowerAdvisor = args.powerAdvisor;
+    mHasPictureProcessing = args.hasPictureProcessing;
+    mMaxLayerPictureProfiles = args.maxLayerPictureProfiles;
     editState().isSecure = args.isSecure;
     editState().isProtected = args.isProtected;
     editState().displaySpace.setBounds(args.pixels);
@@ -80,7 +82,7 @@
     return mId.isVirtual();
 }
 
-std::optional<DisplayId> Display::getDisplayId() const {
+ftl::Optional<DisplayId> Display::getDisplayId() const {
     return mId;
 }
 
@@ -203,15 +205,16 @@
 }
 
 void Display::applyDisplayBrightness(bool applyImmediately) {
-    if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast);
-        displayId && getState().displayBrightness) {
+    if (!getState().displayBrightness) {
+        return;
+    }
+    if (auto displayId = PhysicalDisplayId::tryCast(mId)) {
         auto& hwc = getCompositionEngine().getHwComposer();
-        const status_t result =
-                hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
-                                         getState().displayBrightnessNits,
-                                         Hwc2::Composer::DisplayBrightnessOptions{
-                                                 .applyImmediately = applyImmediately})
-                        .get();
+        status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness,
+                                                   getState().displayBrightnessNits,
+                                                   Hwc2::Composer::DisplayBrightnessOptions{
+                                                           .applyImmediately = applyImmediately})
+                                  .get();
         ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)",
                  getName().c_str(), result, strerror(-result));
     }
@@ -278,6 +281,7 @@
         applyDisplayRequests(changes->displayRequests);
         applyLayerRequestsToLayers(changes->layerRequests);
         applyClientTargetRequests(changes->clientTargetProperty);
+        applyLayerLutsToLayers(changes->layerLuts);
     }
 
     // Determine what type of composition we are doing from the final state
@@ -287,8 +291,8 @@
 }
 
 bool Display::getSkipColorTransform() const {
-    const auto& hwc = getCompositionEngine().getHwComposer();
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
         return hwc.hasDisplayCapability(*halDisplayId,
                                         DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
     }
@@ -359,6 +363,25 @@
             static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
 }
 
+void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) {
+    auto& mapper = getCompositionEngine().getHwComposer().getLutFileDescriptorMapper();
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) {
+            if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) {
+                layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()),
+                                           lutsIt->second);
+            }
+        }
+    }
+
+    mapper.clear();
+}
+
 void Display::executeCommands() {
     const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
     if (mIsDisconnected || !halDisplayIdOpt) {
@@ -437,6 +460,19 @@
     mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
 }
 
+const aidl::android::hardware::graphics::composer3::OverlayProperties*
+Display::getOverlaySupport() {
+    return &getCompositionEngine().getHwComposer().getOverlaySupport();
+}
+
+bool Display::hasPictureProcessing() const {
+    return mHasPictureProcessing;
+}
+
+int32_t Display::getMaxLayerPictureProfiles() const {
+    return mMaxLayerPictureProfiles;
+}
+
 void Display::finishFrame(GpuCompositionResult&& result) {
     // We only need to actually compose the display if:
     // 1) It is being handled by hardware composer, which may need this to
@@ -451,8 +487,8 @@
 }
 
 bool Display::supportsOffloadPresent() const {
-    if (const auto halDisplayId = HalDisplayId::tryCast(mId)) {
-        const auto& hwc = getCompositionEngine().getHwComposer();
+    if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+        auto& hwc = getCompositionEngine().getHwComposer();
         return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT);
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 2d10866..348111d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -127,6 +127,9 @@
     }
     dumpVal(out, "colorTransform", colorTransform);
     dumpVal(out, "caching hint", toString(cachingHint));
+    if (pictureProfileHandle) {
+        dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+    }
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 9c145b6..ac252aa 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -33,9 +33,12 @@
 #include <compositionengine/impl/planner/Planner.h>
 #include <ftl/algorithm.h>
 #include <ftl/future.h>
+#include <ftl/optional.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/Time.h>
 
+#include <com_android_graphics_libgui_flags.h>
+
 #include <optional>
 #include <thread>
 
@@ -112,7 +115,7 @@
             mRenderSurface->isValid();
 }
 
-std::optional<DisplayId> Output::getDisplayId() const {
+ftl::Optional<DisplayId> Output::getDisplayId() const {
     return {};
 }
 
@@ -434,7 +437,7 @@
 ftl::Future<std::monostate> Output::present(
         const compositionengine::CompositionRefreshArgs& refreshArgs) {
     const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string {
-        return ftl::Optional(getDisplayId())
+        return getDisplayId()
                 .and_then(PhysicalDisplayId::tryCast)
                 .and_then([&refreshArgs](PhysicalDisplayId id) {
                     return refreshArgs.frameTargets.get(id);
@@ -501,15 +504,6 @@
     updateHwcAsyncWorker();
 }
 
-void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
-    if (bufferIdsToUncache.empty()) {
-        return;
-    }
-    for (auto outputLayer : getOutputLayersOrderedByZ()) {
-        outputLayer->uncacheBuffers(bufferIdsToUncache);
-    }
-}
-
 void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
                                 LayerFESet& layerFESet) {
     auto& outputState = editState();
@@ -777,11 +771,11 @@
 
     // The layer is visible. Either reuse the existing outputLayer if we have
     // one, or create a new one if we do not.
-    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
+    auto outputLayer = ensureOutputLayer(prevOutputLayerIndex, layerFE);
 
     // Store the layer coverage information into the layer state as some of it
     // is useful later.
-    auto& outputLayerState = result->editState();
+    auto& outputLayerState = outputLayer->editState();
     outputLayerState.visibleRegion = visibleRegion;
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
@@ -799,6 +793,54 @@
     }
 }
 
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+    if (bufferIdsToUncache.empty()) {
+        return;
+    }
+    for (auto outputLayer : getOutputLayersOrderedByZ()) {
+        outputLayer->uncacheBuffers(bufferIdsToUncache);
+    }
+}
+
+void Output::commitPictureProfilesToCompositionState() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+    if (!hasPictureProcessing()) {
+        return;
+    }
+    auto compare = [](const ::android::compositionengine::OutputLayer* lhs,
+                      const ::android::compositionengine::OutputLayer* rhs) {
+        return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority();
+    };
+    std::priority_queue<::android::compositionengine::OutputLayer*,
+                        std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)>
+            layersWithProfiles;
+    for (auto outputLayer : getOutputLayersOrderedByZ()) {
+        if (outputLayer->getPictureProfileHandle()) {
+            layersWithProfiles.push(outputLayer);
+        }
+    }
+
+    // TODO(b/337330263): Use the default display picture profile from SurfaceFlinger
+    editState().pictureProfileHandle = PictureProfileHandle::NONE;
+
+    // When layer-specific picture processing is supported, apply as many high priority profiles as
+    // possible to the layers, and ignore the low priority layers.
+    if (getMaxLayerPictureProfiles() > 0) {
+        for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty();
+             layersWithProfiles.pop(), ++i) {
+            layersWithProfiles.top()->commitPictureProfileToCompositionState();
+            layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
+        }
+        // No layer-specific picture processing, so apply the highest priority picture profile to
+        // the entire display.
+    } else if (!layersWithProfiles.empty()) {
+        editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle();
+        layersWithProfiles.top()->getLayerFE().onPictureProfileCommitted();
+    }
+}
+
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
     // The base class does nothing with this call.
 }
@@ -814,16 +856,20 @@
     mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();
     bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;
 
+    auto* properties = getOverlaySupport();
+
     for (auto* layer : getOutputLayersOrderedByZ()) {
         layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
                                       refreshArgs.devOptForceClientComposition ||
                                               forceClientComposition,
-                                      refreshArgs.internalDisplayRotationFlags);
+                                      refreshArgs.internalDisplayRotationFlags,
+                                      properties ? properties->lutProperties : std::nullopt);
 
         if (mLayerRequestingBackgroundBlur == layer) {
             forceClientComposition = false;
         }
     }
+    commitPictureProfilesToCompositionState();
 }
 
 void Output::planComposition() {
@@ -845,17 +891,25 @@
         return;
     }
 
-    if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId())
+    if (auto frameTargetPtrOpt = getDisplayId()
                                          .and_then(PhysicalDisplayId::tryCast)
                                          .and_then([&refreshArgs](PhysicalDisplayId id) {
                                              return refreshArgs.frameTargets.get(id);
                                          })) {
         editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime();
         editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns();
+        const auto debugPresentDelay = frameTargetPtrOpt->get()->debugPresentDelay();
+        if (debugPresentDelay) {
+            SFTRACE_FORMAT_INSTANT("DEBUG delaying presentation by %.2fms",
+                                   debugPresentDelay->ns() / 1e6f);
+            editState().expectedPresentTime += debugPresentDelay->ns();
+        }
     }
     editState().frameInterval = refreshArgs.frameInterval;
     editState().powerCallback = refreshArgs.powerCallback;
 
+    applyPictureProfile();
+
     compositionengine::OutputLayer* peekThroughLayer = nullptr;
     sp<GraphicBuffer> previousOverride = nullptr;
     bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
@@ -1624,13 +1678,7 @@
             releaseFence =
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            layer->getLayerFE().setReleaseFence(releaseFence);
-        } else {
-            layer->getLayerFE()
-                    .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
-                                      outputState.layerFilter.layerStack);
-        }
+        layer->getLayerFE().setReleaseFence(releaseFence);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1638,12 +1686,7 @@
     // supply them with the present fence.
     for (auto& weakLayer : mReleasedLayers) {
         if (const auto layer = weakLayer.promote()) {
-            if (FlagManager::getInstance().ce_fence_promise()) {
-                layer->setReleaseFence(frame.presentFence);
-            } else {
-                layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
-                                        outputState.layerFilter.layerStack);
-            }
+            layer->setReleaseFence(frame.presentFence);
         }
     }
 
@@ -1703,6 +1746,10 @@
     editState().treat170mAsSrgb = enable;
 }
 
+const aidl::android::hardware::graphics::composer3::OverlayProperties* Output::getOverlaySupport() {
+    return nullptr;
+}
+
 bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
     uint64_t lastOutputLayerHash = getState().lastOutputLayerHash;
     uint64_t outputLayerHash = getState().outputLayerHash;
@@ -1781,5 +1828,34 @@
     return getState().displayBrightnessNits / getState().sdrWhitePointNits;
 }
 
+bool Output::hasPictureProcessing() const {
+    return false;
+}
+
+int32_t Output::getMaxLayerPictureProfiles() const {
+    return 0;
+}
+
+void Output::applyPictureProfile() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+
+    // TODO(b/337330263): Move this into the Display class and add a Display unit test.
+    if (!getState().pictureProfileHandle) {
+        return;
+    }
+    if (!getDisplayId()) {
+        return;
+    }
+    if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) {
+        auto& hwc = getCompositionEngine().getHwComposer();
+        const status_t error =
+                hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle);
+        ALOGE_IF(error, "setDisplayPictureProfileHandle failed for %s: %d, (%s)", getName().c_str(),
+                 error, strerror(-error));
+    }
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 408c58c..c21e7d1 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <DisplayHardware/Hal.h>
 #include <android-base/stringprintf.h>
 #include <compositionengine/DisplayColorProfile.h>
@@ -23,10 +24,13 @@
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ui/FloatRect.h>
+#include <ui/HdrRenderTypeUtils.h>
 #include <cstdint>
+#include <limits>
 #include "system/graphics-base-v1.0.h"
 
-#include <ui/HdrRenderTypeUtils.h>
+#include <com_android_graphics_libgui_flags.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -38,6 +42,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
 using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::Luts;
 
 namespace android::compositionengine {
 
@@ -186,35 +191,35 @@
     const auto& layerState = *getLayerFE().getCompositionState();
     const auto& outputState = getOutput().getState();
 
+    // Convert from layer space to layerStackSpace
     // apply the layer's transform, followed by the display's global transform
     // here we're guaranteed that the layer's transform preserves rects
-    Region activeTransparentRegion = layerState.transparentRegionHint;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
-    const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
-    const Rect& bufferSize = layerState.geomBufferSize;
-    Rect activeCrop = layerState.geomCrop;
-    if (!activeCrop.isEmpty() && bufferSize.isValid()) {
-        activeCrop = layerTransform.transform(activeCrop);
-        if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
-            activeCrop.clear();
-        }
-        activeCrop = inverseLayerTransform.transform(activeCrop, true);
-        // This needs to be here as transform.transform(Rect) computes the
-        // transformed rect and then takes the bounding box of the result before
-        // returning. This means
-        // transform.inverse().transform(transform.transform(Rect)) != Rect
-        // in which case we need to make sure the final rect is clipped to the
-        // display bounds.
-        if (!activeCrop.intersect(bufferSize, &activeCrop)) {
-            activeCrop.clear();
-        }
+    Region activeTransparentRegion = layerTransform.transform(layerState.transparentRegionHint);
+    if (!layerState.geomCrop.isEmpty() && layerState.geomBufferSize.isValid()) {
+        FloatRect activeCrop = layerTransform.transform(layerState.geomCrop);
+        activeCrop = activeCrop.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+        const FloatRect& bufferSize =
+                layerTransform.transform(layerState.geomBufferSize.toFloatRect());
+        activeCrop = activeCrop.intersect(bufferSize);
+
         // mark regions outside the crop as transparent
-        activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
-        activeTransparentRegion.orSelf(
-                Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
-        activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
-        activeTransparentRegion.orSelf(
-                Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
+        Rect topRegion = Rect(layerTransform.transform(
+                FloatRect(0, 0, layerState.geomBufferSize.getWidth(), layerState.geomCrop.top)));
+        Rect bottomRegion = Rect(layerTransform.transform(
+                FloatRect(0, layerState.geomCrop.bottom, layerState.geomBufferSize.getWidth(),
+                          layerState.geomBufferSize.getHeight())));
+        Rect leftRegion = Rect(layerTransform.transform(FloatRect(0, layerState.geomCrop.top,
+                                                                 layerState.geomCrop.left,
+                                                                 layerState.geomCrop.bottom)));
+        Rect rightRegion = Rect(layerTransform.transform(
+                FloatRect(layerState.geomCrop.right, layerState.geomCrop.top,
+                          layerState.geomBufferSize.getWidth(), layerState.geomCrop.bottom)));
+
+        activeTransparentRegion.orSelf(topRegion);
+        activeTransparentRegion.orSelf(bottomRegion);
+        activeTransparentRegion.orSelf(leftRegion);
+        activeTransparentRegion.orSelf(rightRegion);
     }
 
     // reduce uses a FloatRect to provide more accuracy during the
@@ -224,19 +229,22 @@
     // Some HWCs may clip client composited input to its displayFrame. Make sure
     // that this does not cut off the shadow.
     if (layerState.forceClientComposition && layerState.shadowSettings.length > 0.0f) {
-        const auto outset = layerState.shadowSettings.length;
+        // RenderEngine currently blurs shadows to smooth out edges, so outset by
+        // 2x the length instead of 1x to compensate
+        const auto outset = layerState.shadowSettings.length * 2;
         geomLayerBounds.left -= outset;
         geomLayerBounds.top -= outset;
         geomLayerBounds.right += outset;
         geomLayerBounds.bottom += outset;
     }
-    Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
-    if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
-        frame.clear();
-    }
-    const ui::Transform displayTransform{outputState.transform};
 
-    return displayTransform.transform(frame);
+    geomLayerBounds = layerTransform.transform(geomLayerBounds);
+    FloatRect frame = reduce(geomLayerBounds, activeTransparentRegion);
+    frame = frame.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+
+    // convert from layerStackSpace to displaySpace
+    const ui::Transform displayTransform{outputState.transform};
+    return Rect(displayTransform.transform(frame));
 }
 
 uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
@@ -281,9 +289,55 @@
     return transform.getOrientation();
 }
 
+void OutputLayer::updateLuts(
+        const LayerFECompositionState& layerFEState,
+        const std::optional<std::vector<std::optional<LutProperties>>>& properties) {
+    auto& luts = layerFEState.luts;
+    if (!luts) {
+        return;
+    }
+
+    auto& state = editState();
+
+    if (!properties) {
+        // GPU composition if no Hwc Luts
+        state.forceClientComposition = true;
+        return;
+    }
+
+    std::vector<LutProperties> hwcLutProperties;
+    for (auto& p : *properties) {
+        if (p) {
+            hwcLutProperties.emplace_back(*p);
+        }
+    }
+
+    for (const auto& inputLut : luts->lutProperties) {
+        bool foundInHwcLuts = false;
+        for (const auto& hwcLut : hwcLutProperties) {
+            if (static_cast<int32_t>(hwcLut.dimension) ==
+                        static_cast<int32_t>(inputLut.dimension) &&
+                hwcLut.size == inputLut.size &&
+                std::find(hwcLut.samplingKeys.begin(), hwcLut.samplingKeys.end(),
+                          static_cast<LutProperties::SamplingKey>(inputLut.samplingKey)) !=
+                        hwcLut.samplingKeys.end()) {
+                foundInHwcLuts = true;
+                break;
+            }
+        }
+        // if any lut properties of luts can not be found in hwcLutProperties,
+        // GPU composition instead
+        if (!foundInHwcLuts) {
+            state.forceClientComposition = true;
+            return;
+        }
+    }
+}
+
 void OutputLayer::updateCompositionState(
         bool includeGeometry, bool forceClientComposition,
-        ui::Transform::RotationFlags internalDisplayRotationFlags) {
+        ui::Transform::RotationFlags internalDisplayRotationFlags,
+        const std::optional<std::vector<std::optional<LutProperties>>> properties) {
     const auto* layerFEState = getLayerFE().getCompositionState();
     if (!layerFEState) {
         return;
@@ -346,11 +400,22 @@
     // For hdr content, treat the white point as the display brightness - HDR content should not be
     // boosted or dimmed.
     // If the layer explicitly requests to disable dimming, then don't dim either.
-    if (hdrRenderType == HdrRenderType::GENERIC_HDR ||
-        getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
-        getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
+    if (getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
+        getOutput().getState().displayBrightnessNits <= 0.f || !layerFEState->dimmingEnabled) {
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
+    } else if (hdrRenderType == HdrRenderType::GENERIC_HDR) {
+        float deviceHeadroom = getOutput().getState().displayBrightnessNits /
+                getOutput().getState().sdrWhitePointNits;
+        float idealizedMaxHeadroom = deviceHeadroom;
+
+        if (FlagManager::getInstance().begone_bright_hlg()) {
+            idealizedMaxHeadroom =
+                    std::min(idealizedMaxHeadroom, getIdealizedMaxHeadroom(state.dataspace));
+        }
+
+        state.dimmingRatio = std::min(idealizedMaxHeadroom / deviceHeadroom, 1.0f);
+        state.whitePointNits = getOutput().getState().displayBrightnessNits * state.dimmingRatio;
     } else {
         float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
         // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
@@ -364,6 +429,8 @@
         state.whitePointNits = layerBrightnessNits;
     }
 
+    updateLuts(*layerFEState, properties);
+
     // These are evaluated every frame as they can potentially change at any
     // time.
     if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -372,6 +439,16 @@
     }
 }
 
+void OutputLayer::commitPictureProfileToCompositionState() {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        return;
+    }
+    const auto* layerState = getLayerFE().getCompositionState();
+    if (layerState) {
+        editState().pictureProfileHandle = layerState->pictureProfileHandle;
+    }
+}
+
 void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z,
                                   bool zIsOverridden, bool isPeekingThrough) {
     const auto& state = getState();
@@ -416,6 +493,8 @@
     writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
                               skipLayer);
 
+    writeLutToHWC(hwcLayer.get(), *outputIndependentState);
+
     if (requestedCompositionType == Composition::SOLID_COLOR) {
         writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
     }
@@ -519,6 +598,40 @@
     }
 }
 
+void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer,
+                                const LayerFECompositionState& outputIndependentState) {
+    if (!outputIndependentState.luts) {
+        return;
+    }
+    auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor();
+    auto lutOffsets = outputIndependentState.luts->offsets;
+    auto& lutProperties = outputIndependentState.luts->lutProperties;
+
+    std::vector<LutProperties> aidlProperties;
+    aidlProperties.reserve(lutProperties.size());
+    for (size_t i = 0; i < lutOffsets.size(); i++) {
+        LutProperties properties;
+        properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension);
+        properties.size = lutProperties[i].size;
+        properties.samplingKeys = {
+                static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)};
+        aidlProperties.emplace_back(properties);
+    }
+
+    Luts luts;
+    luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get()));
+    luts.offsets = lutOffsets;
+    luts.lutProperties = std::move(aidlProperties);
+
+    switch (auto error = hwcLayer->setLuts(luts)) {
+        case hal::Error::NONE:
+            break;
+        default:
+            ALOGE("[%s] Failed to set Luts: %s (%d)", getLayerFE().getDebugName(),
+                  to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
 void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
     const auto& outputDependentState = getState();
 
@@ -567,6 +680,21 @@
         ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(),
               dimmingRatio, to_string(error).c_str(), static_cast<int32_t>(error));
     }
+
+    if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
+        outputDependentState.pictureProfileHandle) {
+        if (auto error =
+                    hwcLayer->setPictureProfileHandle(outputDependentState.pictureProfileHandle);
+            error != hal::Error::NONE) {
+            ALOGE("[%s] Failed to set picture profile handle: %s (%d)", getLayerFE().getDebugName(),
+                  toString(outputDependentState.pictureProfileHandle).c_str(),
+                  static_cast<int32_t>(error));
+        }
+        // Reset the picture profile state, as it needs to be re-committed on each present cycle
+        // when Output decides that the limited picture-processing hardware should be used by this
+        // layer.
+        editState().pictureProfileHandle = PictureProfileHandle::NONE;
+    }
 }
 
 void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
@@ -672,6 +800,16 @@
     }
 }
 
+int64_t OutputLayer::getPictureProfilePriority() const {
+    const auto* layerState = getLayerFE().getCompositionState();
+    return layerState ? layerState->pictureProfilePriority : 0;
+}
+
+const PictureProfileHandle& OutputLayer::getPictureProfileHandle() const {
+    const auto* layerState = getLayerFE().getCompositionState();
+    return layerState ? layerState->pictureProfileHandle : PictureProfileHandle::NONE;
+}
+
 void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
                                         const LayerFECompositionState& outputIndependentState,
                                         bool skipLayer) {
@@ -754,14 +892,14 @@
         return;
     }
 
-    const auto* layerFEState = getLayerFE().getCompositionState();
-    if (!layerFEState) {
+    const auto* layerState = getLayerFE().getCompositionState();
+    if (!layerState) {
         return;
     }
 
     const auto& outputState = getOutput().getState();
 
-    Rect frame = layerFEState->cursorFrame;
+    Rect frame = layerState->cursorFrame;
     frame.intersect(outputState.layerStackSpace.getContent(), &frame);
     Rect position = outputState.transform.transform(frame);
 
@@ -855,6 +993,31 @@
     }
 }
 
+void OutputLayer::applyDeviceLayerLut(
+        ndk::ScopedFileDescriptor lutFileDescriptor,
+        std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) {
+    auto& state = editState();
+    LOG_FATAL_IF(!state.hwc);
+    auto& hwcState = *state.hwc;
+    std::vector<int32_t> offsets;
+    std::vector<int32_t> dimensions;
+    std::vector<int32_t> sizes;
+    std::vector<int32_t> samplingKeys;
+    for (const auto& [offset, properties] : lutOffsetsAndProperties) {
+        // The Lut(s) that comes back through CommandResultPayload should be
+        // only one sampling key.
+        if (properties.samplingKeys.size() == 1) {
+            offsets.emplace_back(offset);
+            dimensions.emplace_back(static_cast<int32_t>(properties.dimension));
+            sizes.emplace_back(static_cast<int32_t>(properties.size));
+            samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0]));
+        }
+    }
+    hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()),
+                                                       std::move(offsets), std::move(dimensions),
+                                                       std::move(sizes), std::move(samplingKeys));
+}
+
 bool OutputLayer::needsFiltering() const {
     const auto& state = getState();
     const auto& sourceCrop = state.sourceCrop;
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index da1f7e4..deef747 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -72,6 +72,9 @@
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "whitePointNits", whitePointNits);
     dumpVal(out, "dimmingRatio", dimmingRatio);
+    if (pictureProfileHandle) {
+        dumpVal(out, "pictureProfile", toString(pictureProfileHandle));
+    }
     dumpVal(out, "override buffer", overrideInfo.buffer.get());
     dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get());
     dumpVal(out, "override display frame", overrideInfo.displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 639164d..3e0c390 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -26,11 +26,8 @@
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
-#include "MockHWComposer.h"
 #include "TimeStats/TimeStats.h"
-#include "gmock/gmock.h"
-
-#include <variant>
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 using namespace com::android::graphics::surfaceflinger;
 
@@ -494,9 +491,6 @@
 };
 
 TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus)
             .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
     EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus)
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 39163ea..c1e59d0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -36,10 +36,10 @@
 #include <ui/Rect.h>
 #include <ui/StaticDisplayInfo.h>
 
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
-#include "MockPowerAdvisor.h"
 #include "ftl/future.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
@@ -133,6 +133,7 @@
         MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
         MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
         MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+        MOCK_METHOD1(applyLayerLutsToLayers, void(const impl::Display::LayerLuts&));
 
         const compositionengine::CompositionEngine& mCompositionEngine;
         impl::OutputCompositionState mState;
@@ -191,7 +192,7 @@
     }
 
     StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    StrictMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make();
@@ -212,6 +213,7 @@
               aidl::android::hardware::graphics::common::Dataspace::UNKNOWN},
              -1.f,
              DimmingStage::NONE},
+            {},
     };
 
     void chooseCompositionStrategy(Display* display) {
@@ -615,6 +617,7 @@
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
             .Times(1);
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
 
     chooseCompositionStrategy(mDisplay.get());
 
@@ -667,6 +670,7 @@
     EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
             .Times(1);
     EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
 
     chooseCompositionStrategy(mDisplay.get());
 
@@ -1031,7 +1035,7 @@
     }
 
     NiceMock<android::mock::HWComposer> mHwComposer;
-    NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    NiceMock<adpf::mock::PowerAdvisor> mPowerAdvisor;
     NiceMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make();
     sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make();
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
deleted file mode 100644
index 0baa79d..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MockHWC2.h"
-
-namespace android::HWC2 {
-
-// This will go away once HWC2::Layer is moved into the "backend" library
-Layer::~Layer() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
-
-} // namespace mock
-} // namespace android::HWC2
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
deleted file mode 100644
index eb6e677..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-#include <ui/Fence.h>
-#include <ui/FloatRect.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/Transform.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <ui/GraphicTypes.h>
-#include "DisplayHardware/HWC2.h"
-
-#include <aidl/android/hardware/graphics/composer3/Composition.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace HWC2 {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-using Error = hal::Error;
-
-class Layer : public HWC2::Layer {
-public:
-    Layer();
-    ~Layer() override;
-
-    MOCK_CONST_METHOD0(getId, hal::HWLayerId());
-
-    MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t));
-    MOCK_METHOD3(setBuffer,
-                 Error(uint32_t, const android::sp<android::GraphicBuffer>&,
-                       const android::sp<android::Fence>&));
-    MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t));
-    MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
-    MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
-    MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color));
-    MOCK_METHOD1(setCompositionType,
-                 Error(aidl::android::hardware::graphics::composer3::Composition));
-    MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
-    MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
-    MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
-    MOCK_METHOD1(setPlaneAlpha, Error(float));
-    MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*));
-    MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&));
-    MOCK_METHOD1(setTransform, Error(hal::Transform));
-    MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
-    MOCK_METHOD1(setZOrder, Error(uint32_t));
-
-    MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
-    MOCK_METHOD3(setLayerGenericMetadata,
-                 Error(const std::string&, bool, const std::vector<uint8_t>&));
-    MOCK_METHOD1(setBrightness, Error(float));
-    MOCK_METHOD1(setBlockingRegion, Error(const android::Region&));
-    MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
-};
-
-} // namespace mock
-} // namespace HWC2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
deleted file mode 100644
index e910c72..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2018 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 <compositionengine/Output.h>
-#include <gmock/gmock.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include "DisplayHardware/HWComposer.h"
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
-namespace android {
-namespace mock {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-class HWComposer : public android::HWComposer {
-public:
-    HWComposer();
-    ~HWComposer() override;
-
-    MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&));
-    MOCK_CONST_METHOD3(getDisplayIdentificationData,
-                       bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
-    MOCK_CONST_METHOD1(hasCapability,
-                       bool(aidl::android::hardware::graphics::composer3::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability,
-                       bool(HalDisplayId,
-                            aidl::android::hardware::graphics::composer3::DisplayCapability));
-
-    MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
-    MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
-    MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
-    MOCK_METHOD3(allocatePhysicalDisplay,
-                 void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>));
-
-    MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
-    MOCK_METHOD(status_t, getDeviceCompositionChanges,
-                (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
-                 Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
-    MOCK_METHOD(status_t, setClientTarget,
-                (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
-                 float),
-                (override));
-    MOCK_METHOD2(presentAndGetReleaseFences,
-                 status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
-    MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
-    MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
-    MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
-    MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
-    MOCK_METHOD1(disconnectDisplay, void(HalDisplayId));
-    MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId));
-    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
-    MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*));
-    MOCK_METHOD3(setOutputBuffer,
-                 status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
-    MOCK_METHOD1(clearReleaseFences, void(HalDisplayId));
-    MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*));
-    MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId));
-    MOCK_CONST_METHOD2(getRenderIntents,
-                       std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode));
-    MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace));
-    MOCK_METHOD4(getDisplayedContentSamplingAttributes,
-                 status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*));
-    MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t));
-    MOCK_METHOD4(getDisplayedContentSample,
-                 status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
-                (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
-                (override));
-    MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*));
-
-    MOCK_METHOD2(onHotplug,
-                 std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
-    MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
-    MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
-    MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
-    MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId));
-    MOCK_CONST_METHOD2(getModes,
-                       std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t));
-    MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId));
-    MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent));
-    MOCK_CONST_METHOD0(isUsingVrComposer, bool());
-    MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId));
-    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId));
-    MOCK_METHOD4(setActiveModeWithConstraints,
-                 status_t(PhysicalDisplayId, hal::HWConfigId,
-                          const hal::VsyncPeriodChangeConstraints&,
-                          hal::VsyncPeriodChangeTimeline*));
-    MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId));
-    MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId));
-    MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId));
-    MOCK_METHOD0(getBootDisplayModeSupport, bool());
-    MOCK_CONST_METHOD0(
-            getHdrConversionCapabilities,
-            std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>());
-    MOCK_METHOD2(setHdrConversionStrategy,
-                 status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy,
-                          aidl::android::hardware::graphics::common::Hdr*));
-    MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool));
-    MOCK_METHOD(status_t, getSupportedContentTypes,
-                (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
-    MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType));
-    MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
-                       const std::unordered_map<std::string, bool>&());
-
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-    MOCK_CONST_METHOD1(dumpOverlayProperties, void(std::string&));
-    MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-
-    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
-    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
-    MOCK_METHOD(bool, isHeadless, (), (const, override));
-
-    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
-                (const, override));
-    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
-                (const, override));
-    MOCK_METHOD2(getDisplayDecorationSupport,
-                 status_t(PhysicalDisplayId,
-                          std::optional<aidl::android::hardware::graphics::common::
-                                                DisplayDecorationSupport>* support));
-    MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds));
-    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
-    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
-                (const, override));
-    MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
-    MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
-                getOverlaySupport, (), (const, override));
-    MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
-    MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
-    MOCK_METHOD(status_t, getRequestedLuts,
-                (PhysicalDisplayId,
-                 std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
-                (override));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
deleted file mode 100644
index 85b9403..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "MockPowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-
-// This will go away once PowerAdvisor is moved into the "backend" library
-PowerAdvisor::~PowerAdvisor() = default;
-
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-PowerAdvisor::PowerAdvisor() = default;
-PowerAdvisor::~PowerAdvisor() = default;
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
deleted file mode 100644
index ed2ffa9..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
-
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "DisplayHardware/PowerAdvisor.h"
-
-namespace android {
-namespace Hwc2 {
-namespace mock {
-
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
-public:
-    PowerAdvisor();
-    ~PowerAdvisor() override;
-
-    MOCK_METHOD(void, init, (), (override));
-    MOCK_METHOD(void, onBootFinished, (), (override));
-    MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
-                (override));
-    MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyCpuLoadUp, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
-    MOCK_METHOD(bool, usePowerHintSession, (), (override));
-    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
-    MOCK_METHOD(bool, supportsGpuReporting, (), (override));
-    MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
-    MOCK_METHOD(void, reportActualWorkDuration, (), (override));
-    MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
-    MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
-    MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override));
-    MOCK_METHOD(void, setGpuFenceTime,
-                (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
-    MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
-                (override));
-    MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
-                (override));
-    MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
-    MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine),
-                (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
-                (override));
-    MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId, TimePoint earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override));
-    MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override));
-    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
-};
-
-} // namespace mock
-} // namespace Hwc2
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 1c54469..dbffe80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <com_android_graphics_libgui_flags.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -23,13 +24,14 @@
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
-
 #include <renderengine/impl/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
+#include <ui/FloatRect.h>
 #include <ui/PixelFormat.h>
-#include "MockHWC2.h"
-#include "MockHWComposer.h"
+
 #include "RegionMatcher.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 
@@ -270,7 +272,7 @@
         mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
         mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
         mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
-        mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomCrop = FloatRect{0, 0, 1920, 1080};
         mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
         mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
@@ -296,20 +298,20 @@
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
-    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
     const Rect expected{100, 200, 300, 500};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
-    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
     mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
     const Rect expected{1420, 100, 1720, 300};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
-    mLayerFEState.geomCrop = Rect{};
+    mLayerFEState.geomCrop = FloatRect{};
     const Rect expected{0, 0, 1920, 1080};
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
@@ -339,7 +341,7 @@
 
     mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
     Rect expected{mLayerFEState.geomLayerBounds};
-    expected.inset(-kShadowRadius, -kShadowRadius, -kShadowRadius, -kShadowRadius);
+    expected.inset(-2 * kShadowRadius, -2 * kShadowRadius, -2 * kShadowRadius, -2 * kShadowRadius);
     EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
@@ -1331,6 +1333,71 @@
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+    mOutputLayer.commitPictureProfileToCompositionState();
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mLayerFEState.compositionType = Composition::DEVICE;
+    mLayerFEState.pictureProfileHandle = PictureProfileHandle(1);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::DEVICE);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1)));
+
+    mOutputLayer.commitPictureProfileToCompositionState();
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls(kExpectedHwcSlot, nullptr, kFence);
+
+    EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0);
+    // No committing of picture profile before writing the state
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
 /*
  * OutputLayer::uncacheBuffers
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index c34168d..442b603 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/stringprintf.h>
+#include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
@@ -34,15 +35,17 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
-#include <cmath>
 #include <cstdint>
 #include <variant>
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 #include <common/FlagManager.h>
 #include <common/test/FlagUtils.h>
 #include "CallOrderStateMachineHelper.h"
-#include "MockHWC2.h"
 #include "RegionMatcher.h"
+#include "mock/DisplayHardware/MockHWC2.h"
+#include "mock/DisplayHardware/MockHWComposer.h"
 
 namespace android::compositionengine {
 namespace {
@@ -143,6 +146,24 @@
     public:
         using impl::Output::injectOutputLayerForTest;
         virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+        virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; }
+
+        virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; }
+        virtual int32_t getMaxLayerPictureProfiles() const override {
+            return mMaxLayerPictureProfiles;
+        }
+
+        void setDisplayIdForTest(DisplayId value) { mId = value; }
+
+        void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; }
+
+        void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; }
+
+    private:
+        ftl::Optional<DisplayId> mId;
+        bool mHasPictureProcessing;
+        int32_t mMaxLayerPictureProfiles;
     };
 
     static std::shared_ptr<Output> createOutput(
@@ -158,6 +179,7 @@
         mOutput->editState().displaySpace.setBounds(
                 ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
     }
 
     void injectOutputLayer(InjectedLayer& layer) {
@@ -170,6 +192,7 @@
 
     static const Rect kDefaultDisplaySize;
 
+    StrictMock<::android::mock::HWComposer> mHwComposer;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
@@ -786,17 +809,20 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer1.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer2.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer3.outputLayer,
+                updateCompositionState(false, false, ui::Transform::ROT_180, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -823,17 +849,17 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -859,17 +885,17 @@
     InjectedLayer layer3;
 
     uint32_t z = 0;
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -897,11 +923,11 @@
     InjectedLayer layer3;
 
     InSequence seq;
-    EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _));
 
     uint32_t z = 0;
     EXPECT_CALL(*layer0.outputLayer,
@@ -3263,57 +3289,9 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-    // Simulate getting release fences from each layer, and ensure they are passed to the
-    // front-end layer interface for each layer correctly.
-
-    mOutput.mState.isEnabled = true;
-
-    // Create three unique fence instances
-    sp<Fence> layer1Fence = sp<Fence>::make();
-    sp<Fence> layer2Fence = sp<Fence>::make();
-    sp<Fence> layer3Fence = sp<Fence>::make();
-
-    Output::FrameFences frameFences;
-    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
-    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
-    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Compare the pointers values of each fence to make sure the correct ones
-    // are passed. This happens to work with the current implementation, but
-    // would not survive certain calls like Fence::merge() which would return a
-    // new instance.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer1Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer1Fence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer2Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer2Fence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
-            .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                     ui::LayerStack) {
-                EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
-            });
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
 TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
     // Simulate getting release fences from each layer, and ensure they are passed to the
     // front-end layer interface for each layer correctly.
-
     mOutput.mState.isEnabled = true;
 
     // Create three unique fence instances
@@ -3350,37 +3328,7 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
-    mOutput.mState.isEnabled = true;
-    mOutput.mState.usesClientComposition = true;
-
-    Output::FrameFences frameFences;
-    frameFences.clientTargetAcquireFence = sp<Fence>::make();
-    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make());
-    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
-    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Fence::merge is called, and since none of the fences are actually valid,
-    // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
-    // This is the best we can do without creating a real kernel fence object.
-    EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed).WillOnce(Return());
-    EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
-    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-}
-
 TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     mOutput.mState.isEnabled = true;
     mOutput.mState.usesClientComposition = true;
 
@@ -3403,62 +3351,7 @@
     mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
 }
 
-TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
-    ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
-
-    mOutput.mState.isEnabled = true;
-    mOutput.mState.usesClientComposition = true;
-
-    // This should happen even if there are no (current) output layers.
-    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
-
-    // Load up the released layers with some mock instances
-    sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make();
-    sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make();
-    sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make();
-    Output::ReleasedLayers layers;
-    layers.push_back(releasedLayer1);
-    layers.push_back(releasedLayer2);
-    layers.push_back(releasedLayer3);
-    mOutput.setReleasedLayers(std::move(layers));
-
-    // Set up a fake present fence
-    sp<Fence> presentFence = sp<Fence>::make();
-    Output::FrameFences frameFences;
-    frameFences.presentFence = presentFence;
-
-    EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
-    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
-
-    // Each released layer should be given the presentFence.
-    EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
-            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
-                                      ui::LayerStack) {
-                EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
-            });
-
-    constexpr bool kFlushEvenWhenDisabled = false;
-    mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
-
-    // After the call the list of released layers should have been cleared.
-    EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
-}
-
 TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) {
-    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
-    ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
-
     mOutput.mState.isEnabled = true;
     mOutput.mState.usesClientComposition = true;
 
@@ -5066,12 +4959,12 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition, unless opaque.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -5100,17 +4993,17 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -5140,17 +5033,17 @@
 
     uint32_t z = 0;
     // Layer requesting blur, or below, should request client composition.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
     EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
@@ -5174,6 +5067,133 @@
     mOutput->writeCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has only one display-global picture processing pipeline
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(0);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // No layer picture profiles should be committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0);
+
+    // Sets display picture profile to the highest priority layer's profile
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2)));
+
+    // Marks only the highest priority layer as committed
+    EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+    EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+    EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted).Times(0);
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Feature flag disabled, skipping";
+    }
+    mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1));
+    // Has 2 layer-specific picture processing pipelines
+    mOutput->setHasPictureProcessingForTest(true);
+    mOutput->setMaxLayerPictureProfilesForTest(2);
+
+    InjectedLayer layer1;
+    injectOutputLayer(layer1);
+    PictureProfileHandle profileForLayer1(1);
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3));
+    EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer1));
+
+    InjectedLayer layer2;
+    injectOutputLayer(layer2);
+    PictureProfileHandle profileForLayer2(2);
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer2));
+
+    InjectedLayer layer3;
+    injectOutputLayer(layer3);
+    PictureProfileHandle profileForLayer3(3);
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle())
+            .WillRepeatedly(ReturnRef(profileForLayer3));
+
+    // Because StrictMock
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _));
+
+    // The two highest priority layers should have their picture profiles committed
+    EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0);
+    EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState);
+    EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState);
+
+    // Marks only the highest priority layers as committed
+    EXPECT_CALL(*layer1.layerFE, onPictureProfileCommitted).Times(0);
+    EXPECT_CALL(*layer2.layerFE, onPictureProfileCommitted);
+    EXPECT_CALL(*layer3.layerFE, onPictureProfileCommitted);
+
+    // No display picture profile is sent
+    EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0);
+
+    mOutput->editState().isEnabled = true;
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
     // In split-screen landscape mode, the screen is rotated 90 degrees, with
     // one layer on the left covering the left side of the output, and one layer
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index 0e9218c..a086aee 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -28,6 +28,7 @@
 #include <ftl/concat.h>
 #include <ftl/expected.h>
 #include <log/log.h>
+#include <utils/Errors.h>
 
 namespace android::display {
 
@@ -177,12 +178,13 @@
     }
 }
 
-bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
-                                               DisplayModeRequest&& desiredMode,
-                                               const hal::VsyncPeriodChangeConstraints& constraints,
-                                               hal::VsyncPeriodChangeTimeline& outTimeline) {
+auto DisplayModeController::initiateModeChange(
+        PhysicalDisplayId displayId, DisplayModeRequest&& desiredMode,
+        const hal::VsyncPeriodChangeConstraints& constraints,
+        hal::VsyncPeriodChangeTimeline& outTimeline) -> ModeChangeResult {
     std::lock_guard lock(mDisplayLock);
-    const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(ModeChangeResult::Aborted)).get();
 
     // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
     // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
@@ -201,13 +203,17 @@
 
     const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
 
-    if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
-                                                   &outTimeline) != OK) {
-        return false;
+    const auto error = mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(),
+                                                                  constraints, &outTimeline);
+    switch (error) {
+        case FAILED_TRANSACTION:
+            return ModeChangeResult::Rejected;
+        case OK:
+            SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+            return ModeChangeResult::Changed;
+        default:
+            return ModeChangeResult::Aborted;
     }
-
-    SFTRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
-    return true;
 }
 
 void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
@@ -283,6 +289,8 @@
     }
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
 auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const
         -> KernelIdleTimerState {
     std::lock_guard lock(mDisplayLock);
@@ -298,4 +306,5 @@
     return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
 }
 
+#pragma clang diagnostic pop
 } // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 9ec603d..af3e909 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -70,6 +70,7 @@
     RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
 
     enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+    enum class ModeChangeResult { Changed, Rejected, Aborted };
 
     DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
             EXCLUDES(mDisplayLock);
@@ -86,9 +87,9 @@
 
     scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
 
-    bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
-                            const hal::VsyncPeriodChangeConstraints&,
-                            hal::VsyncPeriodChangeTimeline& outTimeline)
+    ModeChangeResult initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+                                        const hal::VsyncPeriodChangeConstraints&,
+                                        hal::VsyncPeriodChangeTimeline& outTimeline)
             REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
new file mode 100644
index 0000000..c68020c
--- /dev/null
+++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+
+#include <ui/DisplayId.h>
+
+#include "Utils/Dumper.h"
+
+namespace android::display {
+
+// Immutable state of a virtual display, captured on creation.
+class VirtualDisplaySnapshot {
+public:
+    VirtualDisplaySnapshot(GpuVirtualDisplayId gpuId, std::string uniqueId)
+          : mIsGpu(true), mUniqueId(std::move(uniqueId)), mVirtualId(gpuId) {}
+    VirtualDisplaySnapshot(HalVirtualDisplayId halId, std::string uniqueId)
+          : mIsGpu(false), mUniqueId(std::move(uniqueId)), mVirtualId(halId) {}
+
+    VirtualDisplayId displayId() const { return mVirtualId; }
+    bool isGpu() const { return mIsGpu; }
+
+    void dump(utils::Dumper& dumper) const {
+        using namespace std::string_view_literals;
+
+        dumper.dump("isGpu"sv, mIsGpu ? "true"sv : "false"sv);
+        dumper.dump("uniqueId"sv, mUniqueId);
+    }
+
+private:
+    const bool mIsGpu;
+    const std::string mUniqueId;
+    const VirtualDisplayId mVirtualId;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 402a3d2..c743ea2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,6 +201,10 @@
     return mPowerMode != hal::PowerMode::OFF;
 }
 
+bool DisplayDevice::isRefreshable() const {
+    return mPowerMode == hal::PowerMode::DOZE || mPowerMode == hal::PowerMode::ON;
+}
+
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3e3f558..af2b48f 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -42,7 +42,6 @@
 
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "FrontEnd/DisplayInfo.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
@@ -173,6 +172,7 @@
     hardware::graphics::composer::hal::PowerMode getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode);
     bool isPoweredOn() const;
+    bool isRefreshable() const;
     void tracePowerMode();
 
     // Enables layer caching on this DisplayDevice
@@ -285,6 +285,8 @@
     bool isProtected = false;
     // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
     Fps requestedRefreshRate;
+    int32_t maxLayerPictureProfiles = 0;
+    bool hasPictureProcessing = false;
 
 private:
     static std::atomic<int32_t> sNextSequenceId;
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 66237b9..25f6513 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -37,19 +37,14 @@
 
 namespace android {
 
-using hardware::hidl_handle;
-using hardware::hidl_vec;
-using hardware::Return;
-
 using aidl::android::hardware::graphics::composer3::BnComposerCallback;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::PowerMode;
 using aidl::android::hardware::graphics::composer3::VirtualDisplay;
 
-using aidl::android::hardware::graphics::composer3::CommandResultPayload;
-
 using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
 using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
 using AidlDisplayIdentification =
@@ -525,11 +520,15 @@
 
 Error AidlComposer::getDisplayAttribute(Display display, Config config,
                                         IComposerClient::Attribute attribute, int32_t* outValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     const auto status =
             mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
                                                      translate<int32_t>(config),
                                                      static_cast<AidlDisplayAttribute>(attribute),
                                                      outValue);
+#pragma clang diagnostic pop
+
     if (!status.isOk()) {
         ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
         return static_cast<Error>(status.getServiceSpecificError());
@@ -539,8 +538,13 @@
 
 Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
     std::vector<int32_t> configs;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     const auto status =
             mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
+#pragma clang diagnostic pop
+
     if (!status.isOk()) {
         ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
         return static_cast<Error>(status.getServiceSpecificError());
@@ -1385,7 +1389,7 @@
     return V2_4::Error::NONE;
 }
 
-V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+Error AidlComposer::setActiveConfigWithConstraints(
         Display display, Config config,
         const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
         VsyncPeriodChangeTimeline* outTimeline) {
@@ -1399,10 +1403,10 @@
                                                      &timeline);
     if (!status.isOk()) {
         ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
-        return static_cast<V2_4::Error>(status.getServiceSpecificError());
+        return static_cast<Error>(status.getServiceSpecificError());
     }
     *outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
-    return V2_4::Error::NONE;
+    return Error::NONE;
 }
 
 V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
@@ -1547,7 +1551,8 @@
     return error;
 }
 
-Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) {
+Error AidlComposer::getRequestedLuts(Display display, std::vector<Layer>* outLayers,
+                                     std::vector<DisplayLuts::LayerLut>* outLuts) {
     Error error = Error::NONE;
     mMutex.lock_shared();
     if (auto reader = getReader(display)) {
@@ -1556,10 +1561,15 @@
         error = Error::BAD_DISPLAY;
     }
     mMutex.unlock_shared();
+
+    outLayers->reserve(outLuts->size());
+    for (const auto& layerLut : *outLuts) {
+        outLayers->emplace_back(translate<Layer>(layerLut.layer));
+    }
     return error;
 }
 
-Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) {
+Error AidlComposer::setLayerLuts(Display display, Layer layer, Luts& luts) {
     Error error = Error::NONE;
     mMutex.lock_shared();
     if (auto writer = getWriter(display)) {
@@ -1633,6 +1643,41 @@
     return Error::NONE;
 }
 
+Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) {
+    const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate<int64_t>(display),
+                                                                        outMaxProfiles);
+    if (!status.isOk()) {
+        ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setDisplayPictureProfileId(translate<int64_t>(display), id);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
+Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerPictureProfileId(translate<int64_t>(display),
+                                               translate<int64_t>(layer), id);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
 ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
         REQUIRES_SHARED(mMutex) {
     return mWriters.get(display);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 246223a..6b5ebc5 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -27,11 +27,6 @@
 #include <utility>
 #include <vector>
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 
@@ -43,9 +38,6 @@
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-
 namespace android::Hwc2 {
 
 using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
@@ -53,6 +45,7 @@
 using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::ComposerClientReader;
 using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 class AidlIComposerCallbackWrapper;
@@ -205,7 +198,7 @@
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
+    Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) override;
@@ -245,12 +238,13 @@
     Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
                                 int32_t frameIntervalNs) override;
     Error getRequestedLuts(
-            Display display,
+            Display display, std::vector<Layer>* outLayers,
             std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
                     outLuts) override;
-    Error setLayerLuts(
-            Display display, Layer layer,
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
+    Error setLayerLuts(Display display, Layer layer, Luts& luts) override;
+    Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+    Error setDisplayPictureProfileId(Display, PictureProfileId id) override;
+    Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 7db9a94..ff292fa 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -29,11 +29,15 @@
 #include <math/mat4.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
 #include <utils/StrongPointer.h>
 
+#include "DisplayHardware/Hal.h"
+
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
 #include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
+#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Color.h>
@@ -42,10 +46,8 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
-#include <aidl/android/hardware/graphics/common/Transform.h>
 #include <optional>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -73,9 +75,9 @@
 using types::V1_2::Dataspace;
 using types::V1_2::PixelFormat;
 
+using hardware::graphics::composer::hal::Error;
 using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
 using V2_1::Layer;
 using V2_4::CommandReaderBase;
 using V2_4::CommandWriterBase;
@@ -261,7 +263,7 @@
             Display display, IComposerClient::DisplayConnectionType* outType) = 0;
     virtual V2_4::Error getDisplayVsyncPeriod(Display display,
                                               VsyncPeriodNanos* outVsyncPeriod) = 0;
-    virtual V2_4::Error setActiveConfigWithConstraints(
+    virtual Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) = 0;
@@ -305,9 +307,12 @@
     virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
     virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
                                         int32_t frameIntervalNs) = 0;
-    virtual Error getRequestedLuts(Display display,
+    virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers,
                                    std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0;
-    virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0;
+    virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0;
+    virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0;
+    virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0;
+    virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 224f50e..e90b5b7 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -31,10 +31,11 @@
 #include <common/FlagManager.h>
 #include <scheduler/Fps.h>
 
-#include "DisplayHardware/Hal.h"
+#include "Hal.h"
 
 namespace android {
 
+using aidl::android::hardware::graphics::composer3::OutputType;
 namespace hal = android::hardware::graphics::composer::hal;
 
 class DisplayMode;
@@ -114,6 +115,11 @@
             return *this;
         }
 
+        Builder& setHdrOutputType(OutputType type) {
+            mDisplayMode->mHdrOutputType = type;
+            return *this;
+        }
+
     private:
         float getDefaultDensity() {
             // Default density is based on TVs: 1080p displays get XHIGH density, lower-
@@ -166,6 +172,8 @@
     // without visual interruptions such as a black screen.
     int32_t getGroup() const { return mGroup; }
 
+    OutputType getHdrOutputType() const { return mHdrOutputType; }
+
 private:
     explicit DisplayMode(hal::HWConfigId id) : mHwcId(id) {}
 
@@ -179,21 +187,25 @@
     Dpi mDpi;
     int32_t mGroup = -1;
     std::optional<hal::VrrConfig> mVrrConfig;
+    OutputType mHdrOutputType;
 };
 
 inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& rhs) {
     return lhs.getHwcId() == rhs.getHwcId() && lhs.getResolution() == rhs.getResolution() &&
             lhs.getVsyncRate().getPeriodNsecs() == rhs.getVsyncRate().getPeriodNsecs() &&
-            lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup();
+            lhs.getDpi() == rhs.getDpi() && lhs.getGroup() == rhs.getGroup() &&
+            lhs.getVrrConfig() == rhs.getVrrConfig() &&
+            lhs.getHdrOutputType() == rhs.getHdrOutputType();
 }
 
 inline std::string to_string(const DisplayMode& mode) {
     return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, "
-                              "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}",
+                              "dpi=%.2fx%.2f, group=%d, vrrConfig=%s, supportedHdrTypes=%s}",
                               ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(),
                               mode.getHeight(), to_string(mode.getVsyncRate()).c_str(),
                               mode.getDpi().x, mode.getDpi().y, mode.getGroup(),
-                              to_string(mode.getVrrConfig()).c_str());
+                              to_string(mode.getVrrConfig()).c_str(),
+                              toString(mode.getHdrOutputType()).c_str());
 }
 
 template <typename... DisplayModePtrs>
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index f1fa938..081f4aa 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -31,6 +31,9 @@
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
+#include <ui/PictureProfileHandle.h>
+
+#include "DisplayHardware/Hal.h"
 
 #include <algorithm>
 #include <cinttypes>
@@ -42,7 +45,8 @@
 using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using aidl::android::hardware::graphics::composer3::DisplayLuts;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::LutProperties;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -52,6 +56,7 @@
 using android::GraphicBuffer;
 using android::HdrCapabilities;
 using android::HdrMetadata;
+using android::PictureProfileHandle;
 using android::Rect;
 using android::Region;
 using android::sp;
@@ -609,17 +614,37 @@
     return static_cast<Error>(error);
 }
 
-Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) {
-    std::vector<DisplayLuts::LayerLut> tmpLayerLuts;
-    const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts);
-    for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) {
-        if (layerLut.lut.pfd.get() >= 0) {
-            outLayerLuts->push_back({layerLut.layer,
-                                     Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()),
-                                         layerLut.lut.lutProperties}});
+Error Display::getRequestedLuts(LayerLuts* outLuts,
+                                LutFileDescriptorMapper& lutFileDescriptorMapper) {
+    std::vector<Hwc2::Layer> layerIds;
+    std::vector<DisplayLuts::LayerLut> tmpLuts;
+    const auto error = static_cast<Error>(mComposer.getRequestedLuts(mId, &layerIds, &tmpLuts));
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    uint32_t numElements = layerIds.size();
+    outLuts->clear();
+    for (uint32_t i = 0; i < numElements; ++i) {
+        auto layer = getLayerById(layerIds[i]);
+        if (layer) {
+            auto& layerLut = tmpLuts[i];
+            if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) {
+                const auto& offsets = layerLut.luts.offsets.value();
+                std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties;
+                lutOffsetsAndProperties.reserve(offsets.size());
+                std::transform(offsets.begin(), offsets.end(), layerLut.luts.lutProperties.begin(),
+                               std::back_inserter(lutOffsetsAndProperties),
+                               [](int32_t i, LutProperties j) { return std::make_pair(i, j); });
+                outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties);
+                lutFileDescriptorMapper.emplace_or_replace(layer.get(),
+                                                           ndk::ScopedFileDescriptor(
+                                                                   layerLut.luts.pfd.release()));
+            }
         }
     }
-    return static_cast<Error>(error);
+
+    return Error::NONE;
 }
 
 Error Display::getDisplayDecorationSupport(
@@ -634,6 +659,16 @@
     return static_cast<Error>(error);
 }
 
+Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) {
+    const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles);
+    return static_cast<Error>(error);
+}
+
+Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) {
+    const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId());
+    return static_cast<Error>(error);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
@@ -1057,7 +1092,7 @@
     return static_cast<Error>(intError);
 }
 
-Error Layer::setLuts(std::vector<Lut>& luts) {
+Error Layer::setLuts(aidl::android::hardware::graphics::composer3::Luts& luts) {
     if (CC_UNLIKELY(!mDisplay)) {
         return Error::BAD_DISPLAY;
     }
@@ -1065,6 +1100,15 @@
     return static_cast<Error>(intError);
 }
 
+Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+    const auto intError =
+            mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId());
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8e2aeaf..6740d8a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -20,9 +20,11 @@
 #include <android-base/thread_annotations.h>
 #include <ftl/expected.h>
 #include <ftl/future.h>
+#include <ftl/small_map.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/HdrCapabilities.h>
+#include <ui/PictureProfileHandle.h>
 #include <ui/Region.h>
 #include <ui/StaticDisplayInfo.h>
 #include <utils/Log.h>
@@ -45,7 +47,7 @@
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
-#include <aidl/android/hardware/graphics/composer3/Lut.h>
+#include <aidl/android/hardware/graphics/composer3/Luts.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 
@@ -107,6 +109,14 @@
     virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
     virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0;
 
+    static const int kLutFileDescriptorMapperSize = 20;
+    using LutOffsetAndProperties = std::vector<
+            std::pair<int32_t, aidl::android::hardware::graphics::composer3::LutProperties>>;
+    using LayerLuts =
+            ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>;
+    using LutFileDescriptorMapper =
+            ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+
     [[nodiscard]] virtual hal::Error acceptChanges() = 0;
     [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
     createLayer() = 0;
@@ -183,14 +193,16 @@
             aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
                     outClientTargetProperty) = 0;
     [[nodiscard]] virtual hal::Error getRequestedLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
-                    outLuts) = 0;
+            LayerLuts* outLuts, LutFileDescriptorMapper& lutFileDescriptorMapper) = 0;
     [[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
     [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0;
     [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation(
             Hwc2::AidlTransform* outTransform) const = 0;
+    [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0;
+    [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+            const PictureProfileHandle& handle) = 0;
 };
 
 namespace impl {
@@ -268,13 +280,14 @@
     hal::Error getClientTargetProperty(
             aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
                     outClientTargetProperty) override;
-    hal::Error getRequestedLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
-                    outLuts) override;
+    hal::Error getRequestedLuts(LayerLuts* outLuts,
+                                LutFileDescriptorMapper& lutFileDescriptorMapper) override;
     hal::Error getDisplayDecorationSupport(
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) override;
     hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override;
+    hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override;
+    hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override;
 
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
@@ -369,7 +382,9 @@
     [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0;
     [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
     [[nodiscard]] virtual hal::Error setLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0;
+            aidl::android::hardware::graphics::composer3::Luts& luts) = 0;
+    [[nodiscard]] virtual hal::Error setPictureProfileHandle(
+            const PictureProfileHandle& handle) = 0;
 };
 
 namespace impl {
@@ -420,8 +435,8 @@
     // AIDL HAL
     hal::Error setBrightness(float brightness) override;
     hal::Error setBlockingRegion(const android::Region& region) override;
-    hal::Error setLuts(
-            std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override;
+    hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override;
+    hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override;
 
 private:
     // These are references to data owned by HWComposer, which will outlive
@@ -443,6 +458,7 @@
     android::HdrMetadata mHdrMetadata;
     android::mat4 mColorMatrix;
     uint32_t mBufferSlot;
+    android::PictureProfileHandle profile;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d08e261..55ccdef 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -27,6 +27,7 @@
 
 #include "HWComposer.h"
 
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
 #include <android-base/properties.h>
 #include <common/trace.h>
 #include <compositionengine/Output.h>
@@ -335,7 +336,8 @@
                                       .height = config.height,
                                       .vsyncPeriod = config.vsyncPeriod,
                                       .configGroup = config.configGroup,
-                                      .vrrConfig = config.vrrConfig};
+                                      .vrrConfig = config.vrrConfig,
+                                      .hdrOutputType = config.hdrOutputType};
 
         const DisplayConfiguration::Dpi estimatedDPI =
                 getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
@@ -379,7 +381,7 @@
         const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
         const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
         const DisplayConfiguration::Dpi hwcDpi =
-                DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f,
+                DisplayConfiguration::Dpi{dpiX == -1 ? dpiX : dpiX / 1000.f,
                                           dpiY == -1 ? dpiY : dpiY / 1000.f};
         const DisplayConfiguration::Dpi estimatedDPI =
                 getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
@@ -587,9 +589,14 @@
     error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
     RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX);
 
+    DeviceRequestedChanges::LayerLuts layerLuts;
+    error = hwcDisplay->getRequestedLuts(&layerLuts, mLutFileDescriptorMapper);
+    RETURN_IF_HWC_ERROR_FOR("getRequestedLuts", error, displayId, BAD_INDEX);
+
     outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
                                                std::move(layerRequests),
-                                               std::move(clientTargetProperty)});
+                                               std::move(clientTargetProperty),
+                                               std::move(layerLuts)});
     error = hwcDisplay->acceptChanges();
     RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
 
@@ -728,7 +735,11 @@
     auto error = mDisplayData[displayId].hwcDisplay->setActiveConfigWithConstraints(hwcModeId,
                                                                                     constraints,
                                                                                     outTimeline);
-    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    if (error == hal::Error::CONFIG_FAILED) {
+        RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId,
+                                FAILED_TRANSACTION);
+    }
+    RETURN_IF_HWC_ERROR_FOR("setActiveConfigWithConstraints", error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
 
@@ -978,21 +989,6 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::getRequestedLuts(
-        PhysicalDisplayId displayId,
-        std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) {
-    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-    const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts);
-    if (error == hal::Error::UNSUPPORTED) {
-        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
-    }
-    if (error == hal::Error::BAD_PARAMETER) {
-        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
-    }
-    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
-    return NO_ERROR;
-}
-
 status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
     const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
@@ -1032,10 +1028,33 @@
     return NO_ERROR;
 }
 
+int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) {
+    int32_t maxProfiles = 0;
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles);
+    RETURN_IF_HWC_ERROR(error, displayId, 0);
+    return maxProfiles;
+}
+
+status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId,
+                                                    const PictureProfileHandle& handle) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle);
+    if (error != hal::Error::UNSUPPORTED) {
+        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    }
+    return NO_ERROR;
+}
+
 const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
     return mSupportedLayerGenericMetadata;
 }
 
+ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+HWComposer::getLutFileDescriptorMapper() {
+    return mLutFileDescriptorMapper;
+}
+
 void HWComposer::dumpOverlayProperties(std::string& result) const {
     // dump overlay properties
     result.append("OverlayProperties:\n");
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b95c619..52662cf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -29,6 +29,7 @@
 #include <ftl/future.h>
 #include <ui/DisplayIdentification.h>
 #include <ui/FenceTime.h>
+#include <ui/PictureProfileHandle.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -53,6 +54,8 @@
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
 namespace android {
@@ -64,6 +67,7 @@
 class TestableSurfaceFlinger;
 struct HWComposerTest;
 struct CompositionInfo;
+class PictureProfileHandle;
 
 namespace Hwc2 {
 class Composer;
@@ -90,11 +94,14 @@
                 aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
         using DisplayRequests = hal::DisplayRequest;
         using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+        using LutProperties = aidl::android::hardware::graphics::composer3::LutProperties;
+        using LayerLuts = HWC2::Display::LayerLuts;
 
         ChangedTypes changedTypes;
         DisplayRequests displayRequests;
         LayerRequests layerRequests;
         ClientTargetProperty clientTargetProperty;
+        LayerLuts layerLuts;
     };
 
     struct HWCDisplayMode {
@@ -106,12 +113,14 @@
         float dpiY = -1.f;
         int32_t configGroup = -1;
         std::optional<hal::VrrConfig> vrrConfig;
+        OutputType hdrOutputType;
 
         friend std::ostream& operator<<(std::ostream& os, const HWCDisplayMode& mode) {
             return os << "id=" << mode.hwcId << " res=" << mode.width << "x" << mode.height
                       << " vsyncPeriod=" << mode.vsyncPeriod << " dpi=" << mode.dpiX << "x"
                       << mode.dpiY << " group=" << mode.configGroup
-                      << " vrrConfig=" << to_string(mode.vrrConfig).c_str();
+                      << " vrrConfig=" << to_string(mode.vrrConfig).c_str()
+                      << " hdrOutputType=" << toString(mode.hdrOutputType);
         }
     };
 
@@ -292,7 +301,7 @@
     virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
     virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
 
-    // Composer 3.0
+    // AIDL Composer
     virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0;
     virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0;
     virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0;
@@ -311,18 +320,17 @@
     virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
     virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
                                            Fps frameInterval) = 0;
-
-    // Composer 4.0
-    virtual status_t getRequestedLuts(
-            PhysicalDisplayId,
-            std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0;
+    virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0;
+    virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0;
+    virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+                                                    const PictureProfileHandle& handle) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
                               const android::HWComposer::DeviceRequestedChanges& rhs) {
     return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
             lhs.layerRequests == rhs.layerRequests &&
-            lhs.clientTargetProperty == rhs.clientTargetProperty;
+            lhs.clientTargetProperty == rhs.clientTargetProperty && lhs.layerLuts == rhs.layerLuts;
 }
 
 namespace impl {
@@ -479,12 +487,10 @@
     status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
     status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
                                    Fps frameInterval) override;
-
-    // Composer 4.0
-    status_t getRequestedLuts(
-            PhysicalDisplayId,
-            std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
-            override;
+    HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override;
+    int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override;
+    status_t setDisplayPictureProfileHandle(PhysicalDisplayId,
+                                            const android::PictureProfileHandle& profile) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
@@ -571,6 +577,8 @@
     const size_t mMaxVirtualDisplayDimension;
     const bool mUpdateDeviceProductInfoOnHotplugReconnect;
     bool mEnableVrrTimeout;
+
+    HWC2::Display::LutFileDescriptorMapper mLutFileDescriptorMapper;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index e3d9622..568d758 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -17,16 +17,21 @@
 #pragma once
 
 #include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <android/hardware/graphics/composer/2.4/types.h>
 
 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <aidl/android/hardware/graphics/common/Hdr.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
 #include <aidl/android/hardware/graphics/composer3/VrrConfig.h>
 
+#include <ftl/enum.h>
+
 #define ERROR_HAS_CHANGES 5
 
 namespace android {
@@ -46,7 +51,6 @@
 using types::V1_2::Dataspace;
 using types::V1_2::PixelFormat;
 
-using V2_1::Error;
 using V2_4::IComposer;
 using V2_4::IComposerCallback;
 using V2_4::IComposerClient;
@@ -78,6 +82,22 @@
 using DisplayConfiguration = V3_0::DisplayConfiguration;
 using VrrConfig = V3_0::VrrConfig;
 
+enum class Error : int32_t {
+    NONE = static_cast<int32_t>(V2_1::Error::NONE),
+    BAD_CONFIG = static_cast<int32_t>(V2_1::Error::BAD_CONFIG),
+    BAD_DISPLAY = static_cast<int32_t>(V2_1::Error::BAD_DISPLAY),
+    BAD_LAYER = static_cast<int32_t>(V2_1::Error::BAD_LAYER),
+    BAD_PARAMETER = static_cast<int32_t>(V2_1::Error::BAD_PARAMETER),
+    NO_RESOURCES = static_cast<int32_t>(V2_1::Error::NO_RESOURCES),
+    NOT_VALIDATED = static_cast<int32_t>(V2_1::Error::NOT_VALIDATED),
+    UNSUPPORTED = static_cast<int32_t>(V2_1::Error::UNSUPPORTED),
+    SEAMLESS_NOT_ALLOWED = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_ALLOWED),
+    SEAMLESS_NOT_POSSIBLE = static_cast<int32_t>(V2_4::Error::SEAMLESS_NOT_POSSIBLE),
+    CONFIG_FAILED = V3_0::IComposerClient::EX_CONFIG_FAILED,
+    PICTURE_PROFILE_MAX_EXCEEDED = V3_0::IComposerClient::EX_PICTURE_PROFILE_MAX_EXCEEDED,
+    ftl_last = PICTURE_PROFILE_MAX_EXCEEDED
+};
+
 } // namespace hardware::graphics::composer::hal
 
 inline bool hasChangesError(hardware::graphics::composer::hal::Error error) {
@@ -210,7 +230,11 @@
 }
 
 inline std::string to_string(hardware::graphics::composer::hal::Error error) {
-    return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
+    // 5 is reserved for historical reason, during validation 5 means has changes.
+    if (hasChangesError(error)) {
+        return "HAS_CHANGES";
+    }
+    return ftl::enum_string(error);
 }
 
 // For utils::Dumper ADL.
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index ee1e07a..5703a2d 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -27,6 +27,7 @@
 #include <SurfaceFlingerProperties.h>
 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
 #include <android/binder_manager.h>
+#include <android/hardware/graphics/composer/2.1/types.h>
 #include <common/trace.h>
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
 #include <hidl/HidlTransportSupport.h>
@@ -47,7 +48,7 @@
 using aidl::android::hardware::graphics::composer3::DimmingStage;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 using aidl::android::hardware::graphics::composer3::DisplayLuts;
-using aidl::android::hardware::graphics::composer3::Lut;
+using aidl::android::hardware::graphics::composer3::Luts;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -173,7 +174,7 @@
 };
 
 // assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_1::Error kDefaultError = V2_1::Error::NO_RESOURCES;
 constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
 
 template <typename T, typename U>
@@ -181,7 +182,7 @@
     return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
 }
 
-Error unwrapRet(Return<Error>& ret) {
+V2_1::Error unwrapRet(Return<V2_1::Error>& ret) {
     return unwrapRet(ret, kDefaultError);
 }
 
@@ -235,7 +236,7 @@
         });
     } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
         composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError == Error::NONE) {
+            if (tmpError == V2_1::Error::NONE) {
                 mClient = tmpClient;
                 mClient_2_2 = tmpClient;
                 mClient_2_3 = tmpClient;
@@ -243,7 +244,7 @@
         });
     } else {
         mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
-            if (tmpError != Error::NONE) {
+            if (tmpError != V2_1::Error::NONE) {
                 return;
             }
 
@@ -325,14 +326,14 @@
 Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
                                          Display* outDisplay) {
     const uint32_t bufferSlotCount = 1;
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_2) {
         mClient_2_2->createVirtualDisplay_2_2(width, height,
                                               static_cast<types::V1_1::PixelFormat>(*format),
                                               bufferSlotCount,
                                               [&](const auto& tmpError, const auto& tmpDisplay,
                                                   const auto& tmpFormat) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -346,7 +347,7 @@
                                       bufferSlotCount,
                                       [&](const auto& tmpError, const auto& tmpDisplay,
                                           const auto& tmpFormat) {
-                                          error = tmpError;
+                                          error = static_cast<Error>(tmpError);
                                           if (error != Error::NONE) {
                                               return;
                                           }
@@ -361,7 +362,7 @@
 
 Error HidlComposer::destroyVirtualDisplay(Display display) {
     auto ret = mClient->destroyVirtualDisplay(display);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::acceptDisplayChanges(Display display) {
@@ -371,10 +372,10 @@
 }
 
 Error HidlComposer::createLayer(Display display, Layer* outLayer) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->createLayer(display, kMaxLayerBufferCount,
                          [&](const auto& tmpError, const auto& tmpLayer) {
-                             error = tmpError;
+                             error = static_cast<Error>(tmpError);
                              if (error != Error::NONE) {
                                  return;
                              }
@@ -387,13 +388,13 @@
 
 Error HidlComposer::destroyLayer(Display display, Layer layer) {
     auto ret = mClient->destroyLayer(display, layer);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -412,11 +413,11 @@
 }
 
 Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
 
     if (mClient_2_3) {
         mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -425,7 +426,7 @@
         });
     } else if (mClient_2_2) {
         mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -436,7 +437,7 @@
         });
     } else {
         mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
-            error = tmpError;
+            error = static_cast<Error>(tmpError);
             if (error != Error::NONE) {
                 return;
             }
@@ -451,7 +452,7 @@
 
 Error HidlComposer::getDisplayAttribute(Display display, Config config,
                                         IComposerClient::Attribute attribute, int32_t* outValue) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_4) {
         mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
                                              [&](const auto& tmpError, const auto& tmpValue) {
@@ -466,7 +467,7 @@
         mClient->getDisplayAttribute(display, config,
                                      static_cast<V2_1::IComposerClient::Attribute>(attribute),
                                      [&](const auto& tmpError, const auto& tmpValue) {
-                                         error = tmpError;
+                                         error = static_cast<Error>(tmpError);
                                          if (error != Error::NONE) {
                                              return;
                                          }
@@ -479,9 +480,9 @@
 }
 
 Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -499,9 +500,9 @@
 }
 
 Error HidlComposer::getDisplayName(Display display, std::string* outName) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -520,9 +521,9 @@
 }
 
 Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -541,14 +542,14 @@
 Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                        float* outMaxLuminance, float* outMaxAverageLuminance,
                                        float* outMinLuminance) {
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_3) {
         mClient_2_3->getHdrCapabilities_2_3(display,
                                             [&](const auto& tmpError, const auto& tmpHdrTypes,
                                                 const auto& tmpMaxLuminance,
                                                 const auto& tmpMaxAverageLuminance,
                                                 const auto& tmpMinLuminance) {
-                                                error = tmpError;
+                                                error = static_cast<Error>(tmpError);
                                                 if (error != Error::NONE) {
                                                     return;
                                                 }
@@ -564,7 +565,7 @@
                                         const auto& tmpMaxLuminance,
                                         const auto& tmpMaxAverageLuminance,
                                         const auto& tmpMinLuminance) {
-                                        error = tmpError;
+                                        error = static_cast<Error>(tmpError);
                                         if (error != Error::NONE) {
                                             return;
                                         }
@@ -606,7 +607,7 @@
 
 Error HidlComposer::setActiveConfig(Display display, Config config) {
     auto ret = mClient->setActiveConfig(display, config);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
@@ -625,7 +626,7 @@
 }
 
 Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
-    hardware::Return<Error> ret(kDefaultError);
+    hardware::Return<V2_1::Error> ret(kDefaultError);
     if (mClient_2_3) {
         ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
     } else if (mClient_2_2) {
@@ -634,7 +635,7 @@
     } else {
         ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
     }
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setColorTransform(Display display, const float* matrix) {
@@ -654,25 +655,25 @@
 }
 
 Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-    Return<Error> ret(Error::UNSUPPORTED);
+    Return<V2_1::Error> ret(V2_1::Error::UNSUPPORTED);
     if (mClient_2_2) {
         ret = mClient_2_2->setPowerMode_2_2(display, mode);
     } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
         ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
     }
 
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
     auto ret = mClient->setVsyncEnabled(display, enabled);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::setClientTargetSlotCount(Display display) {
     const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
     auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
-    return unwrapRet(ret);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
@@ -903,7 +904,7 @@
     // set up new input command queue if necessary
     if (queueChanged) {
         auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
-        auto error = unwrapRet(ret);
+        auto error = static_cast<Error>(unwrapRet(ret));
         if (error != Error::NONE) {
             mWriter.reset();
             return error;
@@ -915,17 +916,17 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     hardware::Return<void> ret;
     auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
                              const auto& tmpOutLength, const auto& tmpOutHandles) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
 
         // set up new output command queue if necessary
         if (error == Error::NONE && tmpOutChanged) {
-            error = kDefaultError;
+            error = static_cast<Error>(kDefaultError);
             mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
-                error = tmpError;
+                error = static_cast<Error>(tmpError);
                 if (error != Error::NONE) {
                     return;
                 }
@@ -1000,11 +1001,11 @@
         return keys;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     if (mClient_2_3) {
         mClient_2_3->getPerFrameMetadataKeys_2_3(display,
                                                  [&](const auto& tmpError, const auto& tmpKeys) {
-                                                     error = tmpError;
+                                                     error = static_cast<Error>(tmpError);
                                                      if (error != Error::NONE) {
                                                          ALOGW("getPerFrameMetadataKeys failed "
                                                                "with %d",
@@ -1016,7 +1017,7 @@
     } else {
         mClient_2_2
                 ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
-                    error = tmpError;
+                    error = static_cast<Error>(tmpError);
                     if (error != Error::NONE) {
                         ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
                         return;
@@ -1039,10 +1040,10 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
 
     auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
-        error = tmpError;
+        error = static_cast<Error>(tmpError);
         if (error != Error::NONE) {
             return;
         }
@@ -1066,10 +1067,10 @@
         return Error::NONE;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
                                               [&](const auto& tmpError, const auto& tmpMatrix) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -1087,11 +1088,11 @@
         return Error::UNSUPPORTED;
     }
 
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayIdentificationData(display,
                                               [&](const auto& tmpError, const auto& tmpPort,
                                                   const auto& tmpData) {
-                                                  error = tmpError;
+                                                  error = static_cast<Error>(tmpError);
                                                   if (error != Error::NONE) {
                                                       return;
                                                   }
@@ -1123,13 +1124,13 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayedContentSamplingAttributes(display,
                                                        [&](const auto tmpError,
                                                            const auto& tmpFormat,
                                                            const auto& tmpDataspace,
                                                            const auto& tmpComponentMask) {
-                                                           error = tmpError;
+                                                           error = static_cast<Error>(tmpError);
                                                            if (error == Error::NONE) {
                                                                *outFormat = tmpFormat;
                                                                *outDataspace = tmpDataspace;
@@ -1149,8 +1150,9 @@
 
     auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
                           : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
-    return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
-                                                           maxFrames);
+    auto ret = mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+                                                               maxFrames);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
@@ -1161,12 +1163,12 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    Error error = kDefaultError;
+    Error error = static_cast<Error>(kDefaultError);
     mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
                                            [&](const auto tmpError, auto tmpNumFrames,
                                                const auto& tmpSamples0, const auto& tmpSamples1,
                                                const auto& tmpSamples2, const auto& tmpSamples3) {
-                                               error = tmpError;
+                                               error = static_cast<Error>(tmpError);
                                                if (error == Error::NONE) {
                                                    outStats->numFrames = tmpNumFrames;
                                                    outStats->component_0_sample = tmpSamples0;
@@ -1196,7 +1198,8 @@
     if (!mClient_2_3) {
         return Error::UNSUPPORTED;
     }
-    return mClient_2_3->setDisplayBrightness(display, brightness);
+    auto ret = mClient_2_3->setDisplayBrightness(display, brightness);
+    return static_cast<Error>(unwrapRet(ret));
 }
 
 // Composer HAL 2.4
@@ -1273,19 +1276,18 @@
     return error;
 }
 
-V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+Error HidlComposer::setActiveConfigWithConstraints(
         Display display, Config config,
         const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
         VsyncPeriodChangeTimeline* outTimeline) {
-    using Error = V2_4::Error;
     if (!mClient_2_4) {
         return Error::UNSUPPORTED;
     }
 
-    Error error = kDefaultError_2_4;
+    Error error = static_cast<Error>(kDefaultError_2_4);
     mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
                                                 [&](const auto& tmpError, const auto& tmpTimeline) {
-                                                    error = tmpError;
+                                                    error = static_cast<Error>(tmpError);
                                                     if (error != Error::NONE) {
                                                         return;
                                                     }
@@ -1410,11 +1412,12 @@
     return Error::NONE;
 }
 
-Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) {
+Error HidlComposer::getRequestedLuts(Display, std::vector<Layer>*,
+                                     std::vector<DisplayLuts::LayerLut>*) {
     return Error::NONE;
 }
 
-Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) {
+Error HidlComposer::setLayerLuts(Display, Layer, Luts&) {
     return Error::NONE;
 }
 
@@ -1445,6 +1448,18 @@
                      "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL");
 }
 
+Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) {
+    return Error::UNSUPPORTED;
+}
+
 void HidlComposer::registerCallback(ComposerCallback& callback) {
     const bool vsyncSwitchingSupported =
             isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 701a54b..42ba9a9 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -60,7 +60,6 @@
 
 using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
 using V2_1::Layer;
 using V2_4::CommandReaderBase;
 using V2_4::CommandWriterBase;
@@ -308,7 +307,7 @@
     V2_4::Error getDisplayConnectionType(Display display,
                                          IComposerClient::DisplayConnectionType* outType) override;
     V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
+    Error setActiveConfigWithConstraints(
             Display display, Config config,
             const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
             VsyncPeriodChangeTimeline* outTimeline) override;
@@ -352,11 +351,14 @@
     Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
     Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
     Error getRequestedLuts(
-            Display,
+            Display, std::vector<Layer>*,
             std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
             override;
     Error setLayerLuts(Display, Layer,
-                       std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override;
+                       aidl::android::hardware::graphics::composer3::Luts&) override;
+    Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override;
+    Error setDisplayPictureProfileId(Display, PictureProfileId) override;
+    Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
deleted file mode 100644
index 3b60952..0000000
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <log/log.h>
-
-#include "EventLog.h"
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
-
-
-EventLog::EventLog() {
-}
-
-void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
-                                   size_t numDurations) {
-    EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
-    buffer.startList(1 + numDurations);
-    buffer.writeString(name);
-    for (size_t i = 0; i < numDurations; i++) {
-        buffer.writeInt32(durations[i]);
-    }
-    buffer.endList();
-    buffer.log();
-}
-
-void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
-                                 size_t numDurations) {
-    EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
-}
-
-// ---------------------------------------------------------------------------
-
-EventLog::TagBuffer::TagBuffer(int32_t tag)
-    : mPos(0), mTag(tag), mOverflow(false) {
-}
-
-void EventLog::TagBuffer::log() {
-    if (mOverflow) {
-        ALOGW("couldn't log to binary event log: overflow.");
-    } else if (android_bWriteLog(mTag, mStorage, mPos) < 0) {
-        ALOGE("couldn't log to EventLog: %s", strerror(errno));
-    }
-    // purge the buffer
-    mPos = 0;
-    mOverflow = false;
-}
-
-void EventLog::TagBuffer::startList(int8_t count) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(count);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_LIST;
-    mStorage[mPos + 1] = count;
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::endList() {
-    if (mOverflow) return;
-    const size_t needed = 1;
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = '\n';
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt32(int32_t value) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(value);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_INT;
-    memcpy(&mStorage[mPos + 1], &value, sizeof(value));
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt64(int64_t value) {
-    if (mOverflow) return;
-    const size_t needed = 1 + sizeof(value);
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_LONG;
-    memcpy(&mStorage[mPos + 1], &value, sizeof(value));
-    mPos += needed;
-}
-
-void EventLog::TagBuffer::writeString(const std::string_view& value) {
-    if (mOverflow) return;
-    const size_t stringLen = value.length();
-    const size_t needed = 1 + sizeof(int32_t) + stringLen;
-    if (mPos + needed > STORAGE_MAX_SIZE) {
-        mOverflow = true;
-        return;
-    }
-    mStorage[mPos + 0] = EVENT_TYPE_STRING;
-    memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
-    memcpy(&mStorage[mPos + 5], value.data(), stringLen);
-    mPos += needed;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
deleted file mode 100644
index ee3587e..0000000
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-
-#include <cstdint>
-#include <string_view>
-
-namespace android {
-
-class EventLog : public Singleton<EventLog> {
-
-public:
-    static void logFrameDurations(const std::string_view& name, const int32_t* durations,
-                                  size_t numDurations);
-
-protected:
-    EventLog();
-
-private:
-    /*
-     * EventLogBuffer is a helper class to construct an in-memory event log
-     * tag. In this version the buffer is not dynamic, so write operation can
-     * fail if there is not enough space in the temporary buffer.
-     * Once constructed, the buffer can be logger by calling the log()
-     * method.
-     */
-
-    class TagBuffer {
-        enum { STORAGE_MAX_SIZE = 128 };
-        int32_t mPos;
-        int32_t mTag;
-        bool mOverflow;
-        char mStorage[STORAGE_MAX_SIZE];
-    public:
-        explicit TagBuffer(int32_t tag);
-
-        void startList(int8_t count);
-        void endList();
-
-        void writeInt32(int32_t);
-        void writeInt64(int64_t);
-        void writeString(const std::string_view&);
-
-        void log();
-    };
-
-    friend class Singleton<EventLog>;
-    EventLog(const EventLog&);
-    EventLog& operator =(const EventLog&);
-
-    enum { LOGTAG_SF_FRAME_DUR = 60100 };
-    void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
-                             size_t numDurations);
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 47b811b..86d7388 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,6 +29,7 @@
 #include <cinttypes>
 #include <numeric>
 #include <unordered_set>
+#include <vector>
 
 #include "../Jank/JankTracker.h"
 
@@ -378,6 +379,11 @@
     }
 }
 
+void SurfaceFrame::setDesiredPresentTime(nsecs_t desiredPresentTime) {
+    std::scoped_lock lock(mMutex);
+    mActuals.desiredPresentTime = desiredPresentTime;
+}
+
 void SurfaceFrame::setDropTime(nsecs_t dropTime) {
     std::scoped_lock lock(mMutex);
     mDropTime = dropTime;
@@ -997,6 +1003,11 @@
     finalizeCurrentDisplayFrame();
 }
 
+const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames()
+        const {
+    return mPresentFrames;
+}
+
 void FrameTimeline::onCommitNotComposited() {
     SFTRACE_CALL();
     std::scoped_lock lock(mMutex);
@@ -1456,6 +1467,30 @@
             static_cast<float>(totalPresentToPresentWalls);
 }
 
+void FrameTimeline::generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const {
+    std::scoped_lock lock(mMutex);
+
+    // TODO: Include FPS calculation here
+    for (auto displayFrame : mDisplayFrames) {
+        if (!count--) {
+            break;
+        }
+
+        if (displayFrame->getActuals().presentTime <= 0) {
+            continue;
+        }
+
+        for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+            if (surfaceFrame->getLayerId() == layer) {
+                outStats->actualPresentTimesNano.push_back(surfaceFrame->getActuals().presentTime);
+                outStats->desiredPresentTimesNano.push_back(
+                        surfaceFrame->getActuals().desiredPresentTime);
+                outStats->frameReadyTimesNano.push_back(surfaceFrame->getActuals().endTime);
+            }
+        }
+    }
+}
+
 std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& [fence, _] = mPendingPresentFences[i];
@@ -1492,6 +1527,7 @@
         mPendingPresentFences.erase(mPendingPresentFences.begin());
     }
 
+    mPresentFrames.clear();
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
         nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
@@ -1504,6 +1540,13 @@
 
         auto& displayFrame = pendingPresentFence.second;
         displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
+
+        // Surface frames have been jank classified and can be provided to caller
+        // to detect if buffer stuffing is occurring.
+        for (const auto& frame : displayFrame->getSurfaceFrames()) {
+            mPresentFrames.push_back(frame);
+        }
+
         mPreviousPredictionPresentTime =
                 displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
                                     mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index cffb61e..a47bd57 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -85,16 +85,20 @@
  */
 struct TimelineItem {
     TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0,
-                 const nsecs_t presentTime = 0)
-          : startTime(startTime), endTime(endTime), presentTime(presentTime) {}
+                 const nsecs_t presentTime = 0, const nsecs_t desiredPresentTime = 0)
+          : startTime(startTime),
+            endTime(endTime),
+            presentTime(presentTime),
+            desiredPresentTime(desiredPresentTime) {}
 
     nsecs_t startTime;
     nsecs_t endTime;
     nsecs_t presentTime;
+    nsecs_t desiredPresentTime;
 
     bool operator==(const TimelineItem& other) const {
         return startTime == other.startTime && endTime == other.endTime &&
-                presentTime == other.presentTime;
+                presentTime == other.presentTime && desiredPresentTime != other.desiredPresentTime;
     }
 
     bool operator!=(const TimelineItem& other) const { return !(*this == other); }
@@ -183,6 +187,7 @@
     void setActualStartTime(nsecs_t actualStartTime);
     void setActualQueueTime(nsecs_t actualQueueTime);
     void setAcquireFenceTime(nsecs_t acquireFenceTime);
+    void setDesiredPresentTime(nsecs_t desiredPresentTime);
     void setDropTime(nsecs_t dropTime);
     void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
     void setRenderRate(Fps renderRate);
@@ -323,6 +328,11 @@
     virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
                               const std::shared_ptr<FenceTime>& gpuFence) = 0;
 
+    // Provides surface frames that have already been jank classified in the most recent
+    // flush of pending present fences. This allows buffer stuffing detection from SF.
+    virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+            const = 0;
+
     // Tells FrameTimeline that a frame was committed but not composited. This is used to flush
     // all the associated surface frames.
     virtual void onCommitNotComposited() = 0;
@@ -341,6 +351,9 @@
     // containing at least one layer ID.
     virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0;
 
+    // Supports the legacy FrameStats interface
+    virtual void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const = 0;
+
     // Restores the max number of display frames to default. Called by SF backdoor.
     virtual void reset() = 0;
 };
@@ -497,10 +510,13 @@
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
     void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
                       const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
+    const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames()
+            const override;
     void onCommitNotComposited() override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
     float computeFps(const std::unordered_set<int32_t>& layerIds) override;
+    void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const override;
     void reset() override;
 
     // Sets up the perfetto tracing backend and data source.
@@ -543,6 +559,9 @@
     // display frame, this is a good starting size for the vector so that we can avoid the
     // internal vector resizing that happens with push_back.
     static constexpr uint32_t kNumSurfaceFramesInitial = 10;
+    // Presented surface frames that have been jank classified and can
+    // indicate of potential buffer stuffing.
+    std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index ca8cdc3..93d0313 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -26,16 +26,10 @@
 #include <ui/FrameStats.h>
 
 #include "FrameTracker.h"
-#include "EventLog/EventLog.h"
 
 namespace android {
 
-FrameTracker::FrameTracker() :
-        mOffset(0),
-        mNumFences(0),
-        mDisplayPeriod(0) {
-    resetFrameCountersLocked();
-}
+FrameTracker::FrameTracker() : mOffset(0), mNumFences(0), mDisplayPeriod(0) {}
 
 void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
     Mutex::Autolock lock(mMutex);
@@ -73,9 +67,6 @@
 void FrameTracker::advanceFrame() {
     Mutex::Autolock lock(mMutex);
 
-    // Update the statistic to include the frame we just finished.
-    updateStatsLocked(mOffset);
-
     // Advance to the next frame.
     mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
     mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -138,19 +129,12 @@
     }
 }
 
-void FrameTracker::logAndResetStats(const std::string_view& name) {
-    Mutex::Autolock lock(mMutex);
-    logStatsLocked(name);
-    resetFrameCountersLocked();
-}
-
 void FrameTracker::processFencesLocked() const {
     FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
     int& numFences = const_cast<int&>(mNumFences);
 
     for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
-        size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
-        bool updated = false;
+        size_t idx = (mOffset + NUM_FRAME_RECORDS - i) % NUM_FRAME_RECORDS;
 
         const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
         if (rfence != nullptr) {
@@ -158,7 +142,6 @@
             if (records[idx].frameReadyTime < INT64_MAX) {
                 records[idx].frameReadyFence = nullptr;
                 numFences--;
-                updated = true;
             }
         }
 
@@ -169,59 +152,8 @@
             if (records[idx].actualPresentTime < INT64_MAX) {
                 records[idx].actualPresentFence = nullptr;
                 numFences--;
-                updated = true;
             }
         }
-
-        if (updated) {
-            updateStatsLocked(idx);
-        }
-    }
-}
-
-void FrameTracker::updateStatsLocked(size_t newFrameIdx) const {
-    int* numFrames = const_cast<int*>(mNumFrames);
-
-    if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) {
-        size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) %
-                NUM_FRAME_RECORDS;
-
-        if (isFrameValidLocked(prevFrameIdx)) {
-            nsecs_t newPresentTime =
-                    mFrameRecords[newFrameIdx].actualPresentTime;
-            nsecs_t prevPresentTime =
-                    mFrameRecords[prevFrameIdx].actualPresentTime;
-
-            nsecs_t duration = newPresentTime - prevPresentTime;
-            int numPeriods = int((duration + mDisplayPeriod/2) /
-                    mDisplayPeriod);
-
-            for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) {
-                int nextBucket = 1 << (i+1);
-                if (numPeriods < nextBucket) {
-                    numFrames[i]++;
-                    return;
-                }
-            }
-
-            // The last duration bucket is a catch-all.
-            numFrames[NUM_FRAME_BUCKETS-1]++;
-        }
-    }
-}
-
-void FrameTracker::resetFrameCountersLocked() {
-    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
-        mNumFrames[i] = 0;
-    }
-}
-
-void FrameTracker::logStatsLocked(const std::string_view& name) const {
-    for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
-        if (mNumFrames[i] > 0) {
-            EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
-            return;
-        }
     }
 }
 
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index bc412ae..fd6fadc 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -41,8 +41,6 @@
     // frame time history.
     enum { NUM_FRAME_RECORDS = 128 };
 
-    enum { NUM_FRAME_BUCKETS = 7 };
-
     FrameTracker();
 
     // setDesiredPresentTime sets the time at which the current frame
@@ -142,13 +140,6 @@
     // doesn't grow with NUM_FRAME_RECORDS.
     int mNumFences;
 
-    // mNumFrames keeps a count of the number of frames with a duration in a
-    // particular range of vsync periods.  Element n of the array stores the
-    // number of frames with duration in the half-inclusive range
-    // [2^n, 2^(n+1)).  The last element of the array contains the count for
-    // all frames with duration greater than 2^(NUM_FRAME_BUCKETS-1).
-    int32_t mNumFrames[NUM_FRAME_BUCKETS];
-
     // mDisplayPeriod is the display refresh period of the display for which
     // this FrameTracker is gathering information.
     nsecs_t mDisplayPeriod;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 0788d1a..07a5724 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -61,6 +61,7 @@
     ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
     uint32_t parentId = UNASSIGNED_LAYER_ID;
     uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+    std::atomic<int32_t>* pendingBuffers = 0;
 };
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
index 75e4e3a..ffd51a4 100644
--- a/services/surfaceflinger/FrontEnd/LayerHandle.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -28,7 +28,7 @@
 
 LayerHandle::~LayerHandle() {
     if (mFlinger) {
-        mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+        mFlinger->onHandleDestroyed(mLayer, mLayerId);
     }
 }
 
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index d709530..da536b6 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -166,7 +166,8 @@
             }
             out << "(Mirroring) ";
         }
-        out << *mLayer;
+
+        out << *mLayer << " pid=" << mLayer->ownerPid.val() << " uid=" << mLayer->ownerUid.val();
     }
 
     for (size_t i = 0; i < mChildren.size(); i++) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index e5f6b7b..58f6b96 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -250,6 +250,7 @@
     if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
     if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
     if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+    if (contentDirty) reason << " contentDirty";
     return reason.str();
 }
 
@@ -359,8 +360,9 @@
                           uint32_t displayRotationFlags) {
     clientChanges = requested.what;
     changes = requested.changes;
-    contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
-    hasReadyFrame = requested.autoRefresh;
+    autoRefresh = requested.autoRefresh;
+    contentDirty = requested.what & layer_state_t::CONTENT_DIRTY || autoRefresh;
+    hasReadyFrame = autoRefresh;
     sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
     updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage);
 
@@ -411,6 +413,13 @@
     if (forceUpdate || requested.what & layer_state_t::eCropChanged) {
         geomCrop = requested.crop;
     }
+    if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) {
+        pictureProfileHandle = requested.pictureProfileHandle;
+    }
+    if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) {
+        // TODO(b/337330263): Also consider the system-determined priority of the app
+        pictureProfilePriority = requested.appContentPriority;
+    }
 
     if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
         const auto compatibility =
@@ -513,6 +522,10 @@
         isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f;
         blendMode = getBlendMode(requested);
     }
+
+    if (forceUpdate || requested.what & layer_state_t::eLutsChanged) {
+        luts = requested.luts;
+    }
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 398e64a..b8df3ed 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -72,11 +72,12 @@
     bool premultipliedAlpha;
     ui::Transform parentTransform;
     Rect bufferSize;
-    Rect croppedBufferSize;
+    FloatRect croppedBufferSize;
     std::shared_ptr<renderengine::ExternalTexture> externalTexture;
     gui::LayerMetadata layerMetadata;
     gui::LayerMetadata relativeLayerMetadata;
     bool hasReadyFrame; // used in post composition to check if there is another frame ready
+    bool autoRefresh;
     ui::Transform localTransformInverse;
     gui::WindowInfo inputInfo;
     ui::Transform localTransform;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ee605b7..34b1307 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -116,7 +116,7 @@
  * that's already included.
  */
 std::pair<FloatRect, bool> getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) {
-    FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect();
+    FloatRect inputBounds = snapshot.croppedBufferSize;
     if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() &&
         snapshot.localTransform.getType() != ui::Transform::IDENTITY) {
         inputBounds = snapshot.localTransform.transform(inputBounds);
@@ -220,7 +220,7 @@
     }
 
     // Check if the parent has cropped the buffer
-    Rect bufferSize = snapshot.croppedBufferSize;
+    FloatRect bufferSize = snapshot.croppedBufferSize;
     if (!bufferSize.isValid()) {
         snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
         return;
@@ -261,20 +261,25 @@
     }
     snapshot.isVisible = visible;
 
-    // TODO(b/238781169) we are ignoring this compat for now, since we will have
-    // to remove any optimization based on visibility.
+    if (FlagManager::getInstance().skip_invisible_windows_in_input()) {
+        snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+    } else {
+        // TODO(b/238781169) we are ignoring this compat for now, since we will have
+        // to remove any optimization based on visibility.
 
-    // For compatibility reasons we let layers which can receive input
-    // receive input before they have actually submitted a buffer. Because
-    // of this we use canReceiveInput instead of isVisible to check the
-    // policy-visibility, ignoring the buffer state. However for layers with
-    // hasInputInfo()==false we can use the real visibility state.
-    // We are just using these layers for occlusion detection in
-    // InputDispatcher, and obviously if they aren't visible they can't occlude
-    // anything.
-    const bool visibleForInput =
-            snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
-    snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput);
+        // For compatibility reasons we let layers which can receive input
+        // receive input before they have actually submitted a buffer. Because
+        // of this we use canReceiveInput instead of isVisible to check the
+        // policy-visibility, ignoring the buffer state. However for layers with
+        // hasInputInfo()==false we can use the real visibility state.
+        // We are just using these layers for occlusion detection in
+        // InputDispatcher, and obviously if they aren't visible they can't occlude
+        // anything.
+        const bool visibleForInput =
+                snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
+        snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE,
+                                          !visibleForInput);
+    }
     LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false",
           snapshot.getDebugString().c_str());
 }
@@ -314,8 +319,8 @@
 void clearChanges(LayerSnapshot& snapshot) {
     snapshot.changes.clear();
     snapshot.clientChanges = 0;
-    snapshot.contentDirty = false;
-    snapshot.hasReadyFrame = false;
+    snapshot.contentDirty = snapshot.autoRefresh;
+    snapshot.hasReadyFrame = snapshot.autoRefresh;
     snapshot.sidebandStreamHasFrame = false;
     snapshot.surfaceDamage.clear();
 }
@@ -724,10 +729,12 @@
     if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry;
     snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable;
     snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN);
+    // mark the content as dirty if the parent state changes can dirty the child's content (for
+    // example alpha)
+    snapshot.contentDirty |= (snapshot.clientChanges & layer_state_t::CONTENT_DIRTY) != 0;
     snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
             parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
             (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
-
     const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
             snapshot.clientChanges & layer_state_t::eReparent ||
             snapshot.changes.any(RequestedLayerState::Changes::Visibility |
@@ -970,7 +977,7 @@
         parentRoundedCorner.radius.y *= t.getScaleY();
     }
 
-    FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect();
+    FloatRect layerCropRect = snapshot.croppedBufferSize;
     const vec2 radius(requested.cornerRadius, requested.cornerRadius);
     RoundedCornerState layerSettings(layerCropRect, radius);
     const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
@@ -1061,7 +1068,7 @@
             requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds;
     snapshot.geomLayerCrop = parentBounds;
     if (!requested.crop.isEmpty()) {
-        snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect());
+        snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop);
     }
     snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
     snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
@@ -1072,10 +1079,10 @@
             snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
     snapshot.parentTransform = parentSnapshot.geomLayerTransform;
 
-    // Subtract the transparent region and snap to the bounds
-    const Rect bounds =
-            RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion);
     if (requested.potentialCursor) {
+        // Subtract the transparent region and snap to the bounds
+        const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
+                                                        requested.transparentRegion);
         snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
     }
 }
@@ -1155,7 +1162,7 @@
     auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
 
     if (!requested.hasInputInfo()) {
-        snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
+        snapshot.inputInfo.inputConfig |= InputConfig::NO_INPUT_CHANNEL;
     }
     fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
 
@@ -1192,7 +1199,8 @@
         snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
     }
 
-    snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
+    snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
+                                      snapshot.croppedBufferSize.getWidth()};
 
     // If the layer is a clone, we need to crop the input region to cloned root to prevent
     // touches from going outside the cloned area.
@@ -1257,6 +1265,10 @@
     for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
         LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
         if (!snapshot.hasInputInfo()) continue;
+        if (FlagManager::getInstance().skip_invisible_windows_in_input() &&
+            snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
+            continue;
+        }
         visitor(snapshot);
     }
 }
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 5734ccf..8892419 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -56,7 +56,8 @@
         ownerUid(args.ownerUid),
         ownerPid(args.ownerPid),
         parentId(args.parentId),
-        layerIdToMirror(args.layerIdToMirror) {
+        layerIdToMirror(args.layerIdToMirror),
+        pendingBuffers(args.pendingBuffers) {
     layerId = static_cast<int32_t>(args.sequence);
     changes |= RequestedLayerState::Changes::Created;
     metadata.merge(args.metadata);
@@ -96,7 +97,7 @@
     LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
     color.a = 1.0f;
 
-    crop.makeInvalid();
+    crop = {0, 0, -1, -1};
     z = 0;
     layerStack = ui::DEFAULT_LAYER_STACK;
     transformToDisplayInverse = false;
@@ -162,7 +163,7 @@
     uint64_t clientChanges = what | layer_state_t::diff(clientState);
     layer_state_t::merge(clientState);
     what = clientChanges;
-    LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
+    LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges);
 
     if (clientState.what & layer_state_t::eFlagsChanged) {
         if ((oldFlags ^ flags) &
@@ -473,10 +474,10 @@
     return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
 }
 
-Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
-    Rect size = bufferSize;
+FloatRect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
+    FloatRect size = bufferSize.toFloatRect();
     if (!crop.isEmpty() && size.isValid()) {
-        size.intersect(crop, &size);
+        size = size.intersect(crop);
     } else if (!crop.isEmpty()) {
         size = crop;
     }
@@ -560,7 +561,7 @@
         return false;
     }
 
-    if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+    if (hasBufferOrSidebandStream() || fillsColor()) {
         return true;
     }
 
@@ -573,6 +574,15 @@
             windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
+bool RequestedLayerState::hasBufferOrSidebandStream() const {
+    return ((sidebandStream != nullptr) || (externalTexture != nullptr));
+}
+
+bool RequestedLayerState::fillsColor() const {
+    return !hasBufferOrSidebandStream() && color.r >= 0.0_hf && color.g >= 0.0_hf &&
+            color.b >= 0.0_hf;
+}
+
 bool RequestedLayerState::hasBlur() const {
     return backgroundBlurRadius > 0 || blurRegions.size() > 0;
 }
@@ -632,7 +642,7 @@
             layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged |
             layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged |
             layer_state_t::eExtendedRangeBrightnessChanged |
-            layer_state_t::eDesiredHdrHeadroomChanged |
+            layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged |
             (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
                      ? layer_state_t::eFlagsChanged
                      : 0);
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 1d96dff..f974ed3 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -79,7 +79,7 @@
     bool isHiddenByPolicy() const;
     half4 getColor() const;
     Rect getBufferSize(uint32_t displayRotationFlags) const;
-    Rect getCroppedBufferSize(const Rect& bufferSize) const;
+    FloatRect getCroppedBufferSize(const Rect& bufferSize) const;
     Rect getBufferCrop() const;
     std::string getDebugString() const;
     std::string getDebugStringShort() const;
@@ -88,6 +88,8 @@
     bool hasValidRelativeParent() const;
     bool hasInputInfo() const;
     bool needsInputInfo() const;
+    bool hasBufferOrSidebandStream() const;
+    bool fillsColor() const;
     bool hasBlur() const;
     bool hasFrameUpdate() const;
     bool hasReadyFrame() const;
@@ -131,6 +133,7 @@
     uint64_t barrierFrameNumber = 0;
     uint32_t barrierProducerId = 0;
     std::string debugName;
+    std::atomic<int32_t>* pendingBuffers = 0;
 
     // book keeping states
     bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md
index e5f51a5..6258f7e 100644
--- a/services/surfaceflinger/FrontEnd/readme.md
+++ b/services/surfaceflinger/FrontEnd/readme.md
@@ -17,6 +17,29 @@
 This allows control to be delegated to different parts of the system - such as SystemServer,
 SysUI and Apps.
 
+### Layer Drawing Order
+Layers are drawn based on an inorder traversal, treating relative parents as
+direct parents. Negative z-values place layers below their parent, while
+non-negative values place them above. Layers with the same z-value are drawn
+in creation order (newer on top).  However, relying on creation order for
+z-ordering is discouraged; use unique z-values whenever possible for better
+control.
+
+Traversal pseudo code:
+```
+fn traverseBottomToTop(root):
+  for each child node including relative children,
+    sorted by z then layer id, with z less than 0:
+          traverseBottomToTop(childNode)
+
+  visit(root)
+
+  for each child node including relative children,
+    sorted by z then layer id, with z greater than
+    or equal to 0:
+          traverseBottomToTop(childNode)
+```
+
 ### Layer Lifecycle
 Layer is created by a client. The client receives a strong binder reference to the layer
 handle, which will keep the layer alive as long as the client holds the reference. The
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index dcb0812..195461f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -138,7 +138,7 @@
                 args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) {
     ALOGV("Creating Layer %s", getDebugName());
 
-    mDrawingState.crop.makeInvalid();
+    mDrawingState.crop = {0, 0, -1, -1};
     mDrawingState.sequence = 0;
     mDrawingState.transform.set(0, 0);
     mDrawingState.frameNumber = 0;
@@ -154,7 +154,7 @@
     mDrawingState.metadata = args.metadata;
     mDrawingState.frameTimelineInfo = {};
     mDrawingState.postTime = -1;
-    mFrameTracker.setDisplayRefreshPeriod(
+    mDeprecatedFrameTracker.setDisplayRefreshPeriod(
             args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
 
     mOwnerUid = args.ownerUid;
@@ -183,9 +183,16 @@
     mFlinger->mTimeStats->onDestroy(layerId);
     mFlinger->mFrameTracer->onDestroy(layerId);
 
-    mFrameTracker.logAndResetStats(mName);
     mFlinger->onLayerDestroyed(this);
 
+    const auto currentTime = std::chrono::steady_clock::now();
+    if (mBufferInfo.mTimeSinceDataspaceUpdate > std::chrono::steady_clock::time_point::min()) {
+        mFlinger->mLayerEvents.emplace_back(mOwnerUid, getSequence(), mBufferInfo.mDataspace,
+                                            std::chrono::duration_cast<std::chrono::milliseconds>(
+                                                    currentTime -
+                                                    mBufferInfo.mTimeSinceDataspaceUpdate));
+    }
+
     if (mDrawingState.sidebandStream != nullptr) {
         mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
     }
@@ -316,7 +323,7 @@
 
 Rect Layer::getCroppedBufferSize(const State& s) const {
     Rect size = getBufferSize(s);
-    Rect crop = getCrop(s);
+    Rect crop = Rect(getCrop(s));
     if (!crop.isEmpty() && size.isValid()) {
         size.intersect(crop, &size);
     } else if (!crop.isEmpty()) {
@@ -373,7 +380,7 @@
     mTransactionFlags |= mask;
 }
 
-bool Layer::setCrop(const Rect& crop) {
+bool Layer::setCrop(const FloatRect& crop) {
     if (mDrawingState.crop == crop) return false;
     mDrawingState.sequence++;
     mDrawingState.crop = crop;
@@ -465,6 +472,9 @@
                                                                  getSequence(), mName,
                                                                  mTransactionName,
                                                                  /*isBuffer*/ false, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
+
     surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
@@ -483,6 +493,8 @@
             mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
                                                                  /*isBuffer*/ true, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
     surfaceFrame->setActualStartTime(info.startTimeNanos);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
@@ -507,6 +519,8 @@
                                                                  mOwnerPid, mOwnerUid,
                                                                  getSequence(), mName, debugName,
                                                                  /*isBuffer*/ false, gameMode);
+    // Buffer hasn't yet been latched, so use mDrawingState
+    surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime);
     surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
@@ -598,19 +612,42 @@
 }
 
 void Layer::dumpFrameStats(std::string& result) const {
-    mFrameTracker.dumpStats(result);
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        FrameStats fs = FrameStats();
+        getFrameStats(&fs);
+        for (auto desired = fs.desiredPresentTimesNano.begin(),
+                  actual = fs.actualPresentTimesNano.begin(),
+                  ready = fs.frameReadyTimesNano.begin();
+             desired != fs.desiredPresentTimesNano.end() &&
+             actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end();
+             ++desired, ++actual, ++ready) {
+            result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready));
+        }
+
+        result.push_back('\n');
+    } else {
+        mDeprecatedFrameTracker.dumpStats(result);
+    }
 }
 
 void Layer::clearFrameStats() {
-    mFrameTracker.clearStats();
-}
-
-void Layer::logFrameStats() {
-    mFrameTracker.logAndResetStats(mName);
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        mFrameStatsHistorySize = 0;
+    } else {
+        mDeprecatedFrameTracker.clearStats();
+    }
 }
 
 void Layer::getFrameStats(FrameStats* outStats) const {
-    mFrameTracker.getStats(outStats);
+    if (FlagManager::getInstance().deprecate_frame_tracker()) {
+        if (auto ftl = getTimeline()) {
+            float fps = ftl->get().computeFps({getSequence()});
+            ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats);
+            outStats->refreshPeriodNano = Fps::fromValue(fps).getPeriodNsecs();
+        }
+    } else {
+        mDeprecatedFrameTracker.getStats(outStats);
+    }
 }
 
 void Layer::onDisconnect() {
@@ -686,8 +723,20 @@
         listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount);
     }
 
-    if (mBufferReleaseChannel) {
-        mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount);
+    if (!mBufferReleaseChannel) {
+        return;
+    }
+
+    status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence,
+                                                               currentMaxAcquiredBufferCount);
+    if (status != OK) {
+        int error = -status;
+        // callReleaseBufferCallback is called during Layer's destructor. In this case, it's
+        // expected to receive connection errors.
+        if (error != EPIPE && error != ECONNRESET) {
+            ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error,
+                  strerror(error));
+        }
     }
 }
 
@@ -756,54 +805,6 @@
     }
 }
 
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
-                             ui::LayerStack layerStack,
-                             std::function<FenceResult(FenceResult)>&& continuation) {
-    sp<CallbackHandle> ch = findCallbackHandle();
-
-    if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
-        futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
-    }
-
-    if (ch != nullptr) {
-        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-        ch->previousSharedReleaseFences.emplace_back(std::move(futureFenceResult));
-        ch->name = mName;
-    } else if (FlagManager::getInstance().screenshot_fence_preservation()) {
-        // If we didn't get a release callback yet, e.g. some scenarios when capturing screenshots
-        // asynchronously, then make sure we don't drop the fence.
-        mPreviousReleaseFenceAndContinuations.emplace_back(std::move(futureFenceResult),
-                                                           std::move(continuation));
-        std::vector<FenceAndContinuation> mergedFences;
-        sp<Fence> prevFence = nullptr;
-        // For a layer that's frequently screenshotted, try to merge fences to make sure we don't
-        // grow unbounded.
-        for (const auto& futureAndContinuation : mPreviousReleaseFenceAndContinuations) {
-            auto result = futureAndContinuation.future.wait_for(0s);
-            if (result != std::future_status::ready) {
-                mergedFences.emplace_back(futureAndContinuation);
-                continue;
-            }
-
-            mergeFence(getDebugName(),
-                       futureAndContinuation.chain().get().value_or(Fence::NO_FENCE), prevFence);
-        }
-        if (prevFence != nullptr) {
-            mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
-        }
-
-        mPreviousReleaseFenceAndContinuations.swap(mergedFences);
-    }
-
-    if (mBufferInfo.mBuffer) {
-        mPreviouslyPresentedLayerStacks.push_back(layerStack);
-    }
-
-    if (mDrawingState.frameNumber > 0) {
-        mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
-    }
-}
-
 void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     for (const auto& handle : mDrawingState.callbackHandles) {
         handle->bufferReleaseChannel = mBufferReleaseChannel;
@@ -1116,22 +1117,13 @@
             handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
             handle->frameNumber = mDrawingState.frameNumber;
             handle->previousFrameNumber = mDrawingState.previousFrameNumber;
-            if (FlagManager::getInstance().ce_fence_promise() &&
-                mPreviousReleaseBufferEndpoint == handle->listener) {
+            if (mPreviousReleaseBufferEndpoint == handle->listener) {
                 // Add fence from previous screenshot now so that it can be dispatched to the
                 // client.
                 for (auto& [_, future] : mAdditionalPreviousReleaseFences) {
                     handle->previousReleaseFences.emplace_back(std::move(future));
                 }
                 mAdditionalPreviousReleaseFences.clear();
-            } else if (FlagManager::getInstance().screenshot_fence_preservation() &&
-                       mPreviousReleaseBufferEndpoint == handle->listener) {
-                // Add fences from previous screenshots now so that they can be dispatched to the
-                // client.
-                for (const auto& futureAndContinution : mPreviousReleaseFenceAndContinuations) {
-                    handle->previousSharedReleaseFences.emplace_back(futureAndContinution.chain());
-                }
-                mPreviousReleaseFenceAndContinuations.clear();
             }
             // Store so latched time and release fence can be set
             mDrawingState.callbackHandles.push_back(handle);
@@ -1323,8 +1315,17 @@
             }
         }
     }
-    if (lastDataspace != mBufferInfo.mDataspace) {
+    if (lastDataspace != mBufferInfo.mDataspace ||
+        mBufferInfo.mTimeSinceDataspaceUpdate == std::chrono::steady_clock::time_point::min()) {
         mFlinger->mHdrLayerInfoChanged = true;
+        const auto currentTime = std::chrono::steady_clock::now();
+        if (mBufferInfo.mTimeSinceDataspaceUpdate > std::chrono::steady_clock::time_point::min()) {
+            mFlinger->mLayerEvents
+                    .emplace_back(mOwnerUid, getSequence(), lastDataspace,
+                                  std::chrono::duration_cast<std::chrono::milliseconds>(
+                                          currentTime - mBufferInfo.mTimeSinceDataspaceUpdate));
+        }
+        mBufferInfo.mTimeSinceDataspaceUpdate = currentTime;
     }
     if (mBufferInfo.mDesiredHdrSdrRatio != mDrawingState.desiredHdrSdrRatio) {
         mBufferInfo.mDesiredHdrSdrRatio = mDrawingState.desiredHdrSdrRatio;
@@ -1347,7 +1348,7 @@
 }
 
 void Layer::decrementPendingBufferCount() {
-    int32_t pendingBuffers = --mPendingBufferTransactions;
+    int32_t pendingBuffers = --mPendingBuffers;
     tracePendingBufferCount(pendingBuffers);
 }
 
@@ -1381,9 +1382,9 @@
         handle->compositorTiming = compositorTiming;
     }
 
-    // Update mFrameTracker.
+    // Update mDeprecatedFrameTracker.
     nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
-    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+    mDeprecatedFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
@@ -1403,15 +1404,15 @@
         }
     }
 
+    // The SurfaceFrame's AcquireFence is the same as this.
     std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
     if (frameReadyFence->isValid()) {
-        mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
+        mDeprecatedFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
         // There was no fence for this frame, so assume that it was ready
         // to be presented at the desired present time.
-        mFrameTracker.setFrameReadyTime(desiredPresentTime);
+        mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
-
     if (display) {
         const auto activeMode = display->refreshRateSelector().getActiveMode();
         const Fps refreshRate = activeMode.fps;
@@ -1426,7 +1427,7 @@
             mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                presentFence,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
+            mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
         } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId());
                    displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
             // The HWC doesn't support present fences, so use the present timestamp instead.
@@ -1447,11 +1448,12 @@
             mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
                                                    mCurrentFrameNumber, actualPresentTime,
                                                    FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentTime(actualPresentTime);
+            mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime);
         }
     }
 
-    mFrameTracker.advanceFrame();
+    mFrameStatsHistorySize++;
+    mDeprecatedFrameTracker.advanceFrame();
     mBufferInfo.mFrameLatencyNeeded = false;
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9bc557e..c234a75 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,6 +18,7 @@
 
 #include <android/gui/DropInputMode.h>
 #include <android/gui/ISurfaceComposerClient.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
 #include <ftl/small_map.h>
 #include <gui/BufferQueue.h>
 #include <gui/LayerState.h>
@@ -44,6 +45,7 @@
 #include <scheduler/Seamlessness.h>
 
 #include <cstdint>
+#include <functional>
 #include <optional>
 #include <vector>
 
@@ -95,7 +97,7 @@
     struct State {
         int32_t sequence; // changes when visible regions can change
         // Crop is expressed in layer space coordinate.
-        Rect crop;
+        FloatRect crop;
         LayerMetadata metadata;
 
         ui::Dataspace dataspace;
@@ -172,7 +174,7 @@
     // be delayed until the resize completes.
 
     // Buffer space
-    bool setCrop(const Rect& crop);
+    bool setCrop(const FloatRect& crop);
 
     bool setTransform(uint32_t /*transform*/);
     bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -198,7 +200,7 @@
     Region getVisibleRegion(const DisplayDevice*) const;
     void updateLastLatchTime(nsecs_t latchtime);
 
-    Rect getCrop(const Layer::State& s) const { return s.crop; }
+    Rect getCrop(const Layer::State& s) const { return Rect(s.crop); }
 
     // from graphics API
     static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
@@ -242,6 +244,8 @@
         sp<Fence> mFence;
         uint32_t mTransform{0};
         ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        std::chrono::steady_clock::time_point mTimeSinceDataspaceUpdate =
+                std::chrono::steady_clock::time_point::min();
         Rect mCrop;
         PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
         bool mTransformToDisplayInverse{false};
@@ -257,8 +261,6 @@
 
     bool fenceHasSignaled() const;
     void onPreComposition(nsecs_t refreshStartTime);
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
-                          std::function<FenceResult(FenceResult)>&& continuation = nullptr);
 
     // Tracks mLastClientCompositionFence and gets the callback handle for this layer.
     sp<CallbackHandle> findCallbackHandle();
@@ -369,7 +371,7 @@
 
     // See mPendingBufferTransactions
     void decrementPendingBufferCount();
-    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBuffers; }
     std::string getPendingBufferCounterName() { return mBlastTransactionName; }
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                    const sp<GraphicBuffer>& buffer, uint64_t framenumber,
@@ -387,20 +389,6 @@
     // from the layer.
     std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
 
-    struct FenceAndContinuation {
-        ftl::SharedFuture<FenceResult> future;
-        std::function<FenceResult(FenceResult)> continuation;
-
-        ftl::SharedFuture<FenceResult> chain() const {
-            if (continuation) {
-                return ftl::Future(future).then(continuation).share();
-            } else {
-                return future;
-            }
-        }
-    };
-    std::vector<FenceAndContinuation> mPreviousReleaseFenceAndContinuations;
-
     // Release fences for buffers that have not yet received a release
     // callback. A release callback may not be given when capturing
     // screenshots asynchronously. There may be no buffer update for the
@@ -447,8 +435,12 @@
 
     uint32_t mTransactionFlags{0};
 
+    // Leverages FrameTimeline to generate FrameStats. Since FrameTimeline already has the data,
+    // statistical history needs to only be tracked by count of frames.
+    // TODO: Deprecate the '--latency-clear' and get rid of this.
+    std::atomic<uint16_t> mFrameStatsHistorySize;
     // Timestamp history for UIAutomation. Thread safe.
-    FrameTracker mFrameTracker;
+    FrameTracker mDeprecatedFrameTracker;
 
     // main thread
     sp<NativeHandle> mSidebandStream;
@@ -562,7 +554,7 @@
     //     - If the integer increases, a buffer arrived at the server.
     //     - If the integer decreases in latchBuffer, that buffer was latched
     //     - If the integer decreases in setBuffer, a buffer was dropped
-    std::atomic<int32_t> mPendingBufferTransactions{0};
+    std::atomic<int32_t> mPendingBuffers{0};
 
     // Contains requested position and matrix updates. This will be applied if the client does
     // not specify a destination frame.
@@ -570,6 +562,9 @@
 
     std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
     bool mHandleAlive = false;
+    std::optional<std::reference_wrapper<frametimeline::FrameTimeline>> getTimeline() const {
+        return *mFlinger->mFrameTimeline;
+    }
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index b05f0ee..fea7671 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -26,9 +26,7 @@
 
 #include "LayerFE.h"
 #include "SurfaceFlinger.h"
-#include "common/FlagManager.h"
 #include "ui/FenceResult.h"
-#include "ui/LayerStack.h"
 
 namespace android {
 
@@ -84,8 +82,7 @@
     // Ensures that no promise is left unfulfilled before the LayerFE is destroyed.
     // An unfulfilled promise could occur when a screenshot is attempted, but the
     // render area is invalid and there is no memory for the capture result.
-    if (FlagManager::getInstance().ce_fence_promise() &&
-        mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+    if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
         setReleaseFence(Fence::NO_FENCE);
     }
 }
@@ -176,6 +173,7 @@
     layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect;
     // Record the name of the layer for debugging further down the stack.
     layerSettings.name = mSnapshot->name;
+    layerSettings.luts = mSnapshot->luts;
 
     if (hasEffect() && !hasBufferOrSidebandStream()) {
         prepareEffectsClientComposition(layerSettings, targetSettings);
@@ -344,13 +342,15 @@
     caster.shadow = state;
 }
 
-void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
-                               ui::LayerStack layerStack) {
-    mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
+void LayerFE::onPictureProfileCommitted() {
+    mCompositionResult.wasPictureProfileCommitted = true;
+    mCompositionResult.pictureProfileHandle = mSnapshot->pictureProfileHandle;
 }
 
-CompositionResult&& LayerFE::stealCompositionResult() {
-    return std::move(mCompositionResult);
+CompositionResult LayerFE::stealCompositionResult() {
+    CompositionResult result;
+    std::swap(mCompositionResult, result);
+    return result;
 }
 
 const char* LayerFE::getDebugName() const {
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 658f949..9483aeb 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -18,19 +18,24 @@
 
 #include <android/gui/CachingHint.h>
 #include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
+#include <ui/PictureProfileHandle.h>
+
 #include "FrontEnd/LayerSnapshot.h"
 #include "compositionengine/LayerFE.h"
 #include "compositionengine/LayerFECompositionState.h"
 #include "renderengine/LayerSettings.h"
-#include "ui/LayerStack.h"
 
 #include <ftl/future.h>
 
 namespace android {
 
 struct CompositionResult {
-    std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
     sp<Fence> lastClientCompositionFence = nullptr;
+    bool wasPictureProfileCommitted = false;
+    // TODO(b/337330263): Why does LayerFE coming from SF have a null composition state?
+    // It would be better not to duplicate this information
+    PictureProfileHandle pictureProfileHandle = PictureProfileHandle::NONE;
 };
 
 class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
@@ -41,7 +46,6 @@
     // compositionengine::LayerFE overrides
     const compositionengine::LayerFECompositionState* getCompositionState() const override;
     bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
     const char* getDebugName() const override;
     int32_t getSequence() const override;
     bool hasRoundedCorners() const override;
@@ -50,10 +54,11 @@
     const gui::LayerMetadata* getRelativeMetadata() const override;
     std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
-    CompositionResult&& stealCompositionResult();
+    CompositionResult stealCompositionResult();
     ftl::Future<FenceResult> createReleaseFenceFuture() override;
     void setReleaseFence(const FenceResult& releaseFence) override;
     LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
+    void onPictureProfileCommitted() override;
 
     std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
 
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 5eea45b..44cd319 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -106,6 +106,13 @@
     outRect.right = proto.right();
 }
 
+void LayerProtoHelper::readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect) {
+    outRect.left = proto.left();
+    outRect.top = proto.top();
+    outRect.bottom = proto.bottom();
+    outRect.right = proto.right();
+}
+
 void LayerProtoHelper::writeToProto(
         const FloatRect& rect,
         std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto) {
@@ -180,10 +187,6 @@
 void LayerProtoHelper::writeToProto(
         const WindowInfo& inputInfo,
         std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) {
-    if (inputInfo.token == nullptr) {
-        return;
-    }
-
     perfetto::protos::InputWindowInfoProto* proto = getInputWindowInfoProto();
     proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
     proto->set_input_config(inputInfo.inputConfig.get());
@@ -427,7 +430,7 @@
                                        layerInfo->mutable_color_transform());
     }
 
-    LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+    LayerProtoHelper::writeToProto(snapshot.croppedBufferSize,
                                    [&]() { return layerInfo->mutable_source_bounds(); });
     LayerProtoHelper::writeToProto(snapshot.transformedBounds,
                                    [&]() { return layerInfo->mutable_screen_bounds(); });
@@ -455,7 +458,7 @@
         return layerInfo->mutable_requested_position();
     });
 
-    LayerProtoHelper::writeToProto(requestedState.crop,
+    LayerProtoHelper::writeToProto(Rect(requestedState.crop),
                                    [&]() { return layerInfo->mutable_crop(); });
 
     layerInfo->set_is_opaque(snapshot.contentOpaque);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 41ea684..3ca553a 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -44,6 +44,7 @@
                              std::function<perfetto::protos::RectProto*()> getRectProto);
     static void writeToProto(const Rect& rect, perfetto::protos::RectProto* rectProto);
     static void readFromProto(const perfetto::protos::RectProto& proto, Rect& outRect);
+    static void readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect);
     static void writeToProto(const FloatRect& rect,
                              std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto);
     static void writeToProto(const Region& region,
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index ffc1dd7..fa0ecee 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,6 +5,8 @@
 domlaskowski@google.com
 jreck@google.com
 lpy@google.com
+mattbuckley@google.com
+melodymhsu@google.com
 pdwilliams@google.com
 racarr@google.com
 ramindani@google.com
@@ -12,3 +14,4 @@
 sallyqi@google.com
 scroggo@google.com
 vishnun@google.com
+xwxw@google.com
diff --git a/services/surfaceflinger/PowerAdvisor/Android.bp b/services/surfaceflinger/PowerAdvisor/Android.bp
new file mode 100644
index 0000000..4efbcb9
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/Android.bp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+// ADPF uses FMQ which can't build to CPP backend, and is thus not
+// compatible with the rest of SF aidl for this reason
+
+aidl_interface {
+    name: "android.adpf.sessionmanager_aidl",
+    srcs: [
+        "aidl/android/adpf/*.aidl",
+    ],
+    local_include_dir: "aidl",
+    unstable: true,
+    backend: {
+        java: {
+            sdk_version: "module_current",
+            enabled: true,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: true,
+        },
+    },
+}
+
+cc_defaults {
+    name: "poweradvisor_deps",
+    shared_libs: [
+        "libpowermanager",
+        "android.adpf.sessionmanager_aidl-ndk",
+    ],
+}
diff --git a/services/surfaceflinger/PowerAdvisor/OWNERS b/services/surfaceflinger/PowerAdvisor/OWNERS
new file mode 100644
index 0000000..9f40e27
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/ADPF_OWNERS
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
similarity index 92%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
index 334c104..c7d0b2c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <cinttypes>
 #include <cstdint>
+#include <functional>
 #include <optional>
 
 #include <android-base/properties.h>
@@ -33,45 +34,29 @@
 
 #include <binder/IServiceManager.h>
 
-#include "../SurfaceFlingerProperties.h"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+#pragma clang diagnostic pop
 
+#include <common/FlagManager.h>
 #include "PowerAdvisor.h"
-#include "SurfaceFlinger.h"
 
-namespace android {
-namespace Hwc2 {
+namespace hal = aidl::android::hardware::power;
 
-PowerAdvisor::~PowerAdvisor() = default;
+namespace android::adpf::impl {
 
-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 ChannelMessageContents = hal::ChannelMessage::ChannelMessageContents;
+using MsgQueue = android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>;
 using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
 
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
-std::chrono::milliseconds getUpdateTimeout() {
-    // Default to a timeout of 80ms if nothing else is specified
-    static std::chrono::milliseconds timeout =
-            std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80));
-    return timeout;
-}
-
 void traceExpensiveRendering(bool enabled) {
     if (enabled) {
         SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0);
@@ -82,28 +67,30 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
-      : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) {
-    if (getUpdateTimeout() > 0ms) {
-        mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(),
+PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn,
+                           std::chrono::milliseconds timeout)
+      : mPowerHal(std::make_unique<power::PowerHalController>()) {
+    if (timeout > 0ms) {
+        mScreenUpdateTimer.emplace("UpdateImminentTimer", timeout,
                                    /* resetCallback */ nullptr,
                                    /* timeoutCallback */
-                                   [this] {
+                                   [this, disableExpensiveFn = std::move(sfDisableExpensiveFn),
+                                    timeout] {
                                        while (true) {
                                            auto timeSinceLastUpdate = std::chrono::nanoseconds(
                                                    systemTime() - mLastScreenUpdatedTime.load());
-                                           if (timeSinceLastUpdate >= getUpdateTimeout()) {
+                                           if (timeSinceLastUpdate >= timeout) {
                                                break;
                                            }
                                            // We may try to disable expensive rendering and allow
                                            // for sending DISPLAY_UPDATE_IMMINENT hints too early if
                                            // we idled very shortly after updating the screen, so
                                            // make sure we wait enough time.
-                                           std::this_thread::sleep_for(getUpdateTimeout() -
+                                           std::this_thread::sleep_for(timeout -
                                                                        timeSinceLastUpdate);
                                        }
                                        mSendUpdateImminent.store(true);
-                                       mFlinger.disableExpensiveRendering();
+                                       disableExpensiveFn();
                                    });
     }
 }
@@ -132,7 +119,7 @@
 
     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
-        auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
+        auto ret = getPowerHal().setMode(hal::Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
         if (!ret.isOk()) {
             if (ret.isUnsupported()) {
                 mHasExpensiveRendering = false;
@@ -151,7 +138,7 @@
     if (!mBootFinished.load()) {
         return;
     }
-    sendHintSessionHint(SessionHint::CPU_LOAD_UP);
+    sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP);
 }
 
 void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -163,12 +150,12 @@
 
     if (mSendUpdateImminent.exchange(false)) {
         ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
-        sendHintSessionHint(SessionHint::CPU_LOAD_RESET);
+        sendHintSessionHint(hal::SessionHint::CPU_LOAD_RESET);
 
         if (!mHasDisplayUpdateImminent) {
             ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
         } else {
-            auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+            auto ret = getPowerHal().setBoost(hal::Boost::DISPLAY_UPDATE_IMMINENT, 0);
             if (ret.isUnsupported()) {
                 mHasDisplayUpdateImminent = false;
             }
@@ -205,7 +192,7 @@
             FlagManager::getInstance().adpf_use_fmq_channel();
 }
 
-void PowerAdvisor::sendHintSessionHint(SessionHint hint) {
+void PowerAdvisor::sendHintSessionHint(hal::SessionHint hint) {
     if (!mBootFinished || !usePowerHintSession()) {
         ALOGV("Power hint session is not enabled, skip sending session hint");
         return;
@@ -236,11 +223,12 @@
                                                                  static_cast<int32_t>(getuid()),
                                                                  mHintSessionThreadIds,
                                                                  mTargetDuration.ns(),
-                                                                 SessionTag::SURFACEFLINGER,
+                                                                 hal::SessionTag::SURFACEFLINGER,
                                                                  &mSessionConfig);
             if (ret.isOk()) {
                 mHintSession = ret.value();
-                if (FlagManager::getInstance().adpf_use_fmq_channel_fixed()) {
+                if (FlagManager::getInstance().adpf_use_fmq_channel_fixed() &&
+                    FlagManager::getInstance().adpf_fmq_sf()) {
                     setUpFmq();
                 }
             }
@@ -325,7 +313,7 @@
         return;
     }
     SFTRACE_CALL();
-    std::optional<WorkDuration> actualDuration = estimateWorkDuration();
+    std::optional<hal::WorkDuration> actualDuration = estimateWorkDuration();
     if (!actualDuration.has_value() || actualDuration->durationNanos < 0) {
         ALOGV("Failed to send actual work duration, skipping");
         return;
@@ -376,7 +364,7 @@
     mHintSessionQueue.clear();
 }
 
-template <ChannelMessage::ChannelMessageContents::Tag T, class In>
+template <hal::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);
@@ -394,13 +382,13 @@
     }
     for (size_t i = 0; i < count; ++i) {
         if constexpr (T == ChannelMessageContents::Tag::workDuration) {
-            const WorkDuration& duration = contents[i];
-            new (tx.getSlot(i)) ChannelMessage{
+            const hal::WorkDuration& duration = contents[i];
+            new (tx.getSlot(i)) hal::ChannelMessage{
                     .sessionID = static_cast<int32_t>(mSessionConfig.id),
                     .timeStampNanos =
                             (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos,
                     .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration,
-                                                         WorkDurationFixedV1>({
+                                                         hal::WorkDurationFixedV1>({
                             .durationNanos = duration.durationNanos,
                             .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
                             .cpuDurationNanos = duration.cpuDurationNanos,
@@ -408,7 +396,7 @@
                     }),
             };
         } else {
-            new (tx.getSlot(i)) ChannelMessage{
+            new (tx.getSlot(i)) hal::ChannelMessage{
                     .sessionID = static_cast<int32_t>(mSessionConfig.id),
                     .timeStampNanos = ::android::uptimeNanos(),
                     .data = ChannelMessageContents::make<T, In>(std::move(contents[i])),
@@ -571,7 +559,7 @@
     return sortedDisplays;
 }
 
-std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() {
+std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() {
     if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
         return std::nullopt;
     }
@@ -656,7 +644,7 @@
     Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
     Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration);
 
-    WorkDuration duration{
+    hal::WorkDuration duration{
             .timeStampNanos = TimePoint::now().ns(),
             .durationNanos = combinedDuration.ns(),
             .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
@@ -759,6 +747,4 @@
     return *mPowerHal;
 }
 
-} // namespace impl
-} // namespace Hwc2
-} // namespace android
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
similarity index 97%
rename from services/surfaceflinger/DisplayHardware/PowerAdvisor.h
rename to services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
index 1076b2b..458b46d 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <atomic>
-#include <chrono>
+#include <future>
 #include <unordered_map>
 #include <unordered_set>
 
@@ -30,10 +30,8 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #include <aidl/android/hardware/power/IPower.h>
 #include <fmq/AidlMessageQueue.h>
-#include <powermanager/PowerHalController.h>
 #pragma clang diagnostic pop
 
-#include <compositionengine/impl/OutputCompositionState.h>
 #include <scheduler/Time.h>
 #include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
@@ -42,13 +40,16 @@
 
 namespace android {
 
-class SurfaceFlinger;
+namespace power {
+class PowerHalController;
+class PowerHintSessionWrapper;
+} // namespace power
 
-namespace Hwc2 {
+namespace adpf {
 
 class PowerAdvisor {
 public:
-    virtual ~PowerAdvisor();
+    virtual ~PowerAdvisor() = default;
 
     // Initializes resources that cannot be initialized on construction
     virtual void init() = 0;
@@ -113,9 +114,9 @@
 
 // PowerAdvisor is a wrapper around IPower HAL which takes into account the
 // full state of the system when sending out power hints to things like the GPU.
-class PowerAdvisor final : public Hwc2::PowerAdvisor {
+class PowerAdvisor final : public adpf::PowerAdvisor {
 public:
-    PowerAdvisor(SurfaceFlinger& flinger);
+    PowerAdvisor(std::function<void()>&& function, std::chrono::milliseconds timeout);
     ~PowerAdvisor() override;
 
     void init() override;
@@ -159,7 +160,6 @@
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
 
-    SurfaceFlinger& mFlinger;
     std::atomic_bool mSendUpdateImminent = true;
     std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
     std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
@@ -326,5 +326,5 @@
 };
 
 } // namespace impl
-} // namespace Hwc2
+} // namespace adpf
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
similarity index 64%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
copy to services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
index d383283..c1a6a9e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/services/surfaceflinger/PowerAdvisor/aidl/android/adpf/ISessionManager.aidl
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+package android.adpf;
 
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
-      : power::PowerHintSessionWrapper(nullptr) {}
-
-} // namespace android::Hwc2::mock
+/**
+ * Private service for SessionManager to use. Ideally this will
+ * eventually take the role of HintManagerService.
+ */
+interface ISessionManager {
+    oneway void associateSessionToLayers(in int sessionId, in int ownerUid, in IBinder[] layers);
+    oneway void trackedSessionsDied(in int[] sessionId);
+}
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 06c2f26..21d3396 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -353,22 +353,13 @@
                               sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak,
                               RenderArea::Options::CAPTURE_SECURE_LAYERS);
 
-    FenceResult fenceResult;
-    if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) {
-        std::vector<sp<LayerFE>> layerFEs;
-        auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
-                                                                getLayerSnapshotsFn, layerFEs);
-        fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling,
-                                                 kGrayscale, kIsProtected, kAttachGainmap, nullptr,
-                                                 displayState, layerFEs)
-                              .get();
-    } else {
-        fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn,
-                                                       buffer, kRegionSampling, kGrayscale,
-                                                       kIsProtected, kAttachGainmap, nullptr)
-                              .get();
-    }
+    std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+    auto displayState =
+            mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
+    FenceResult fenceResult =
+            mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
+                                       kIsProtected, kAttachGainmap, nullptr, displayState, layers)
+                    .get();
     if (fenceResult.ok()) {
         fenceResult.value()->waitForever(LOG_TAG);
     }
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 218c56e..c6d7160 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -43,11 +43,10 @@
 #include <utils/Errors.h>
 
 #include <common/FlagManager.h>
+#include <scheduler/FrameRateMode.h>
 #include <scheduler/VsyncConfig.h>
-#include "DisplayHardware/DisplayMode.h"
 #include "FrameTimeline.h"
 #include "VSyncDispatch.h"
-#include "VSyncTracker.h"
 
 #include "EventThread.h"
 
@@ -104,6 +103,10 @@
                                 to_string(event.header.displayId).c_str(),
                                 event.hdcpLevelsChange.connectedLevel,
                                 event.hdcpLevelsChange.maxLevel);
+        case DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION:
+            return StringPrintf("ModeRejected{displayId=%s, modeId=%u}",
+                                to_string(event.header.displayId).c_str(),
+                                event.modeRejection.modeId);
         default:
             return "Event{}";
     }
@@ -188,6 +191,18 @@
     };
 }
 
+DisplayEventReceiver::Event makeModeRejection(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    return DisplayEventReceiver::Event{
+            .header =
+                    DisplayEventReceiver::Event::Header{
+                            .type = DisplayEventReceiver::DISPLAY_EVENT_MODE_REJECTION,
+                            .displayId = displayId,
+                            .timestamp = systemTime(),
+                    },
+            .modeRejection.modeId = ftl::to_underlying(modeId),
+    };
+}
+
 } // namespace
 
 EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
@@ -420,14 +435,24 @@
     mCondition.notify_all();
 }
 
+void EventThread::omitVsyncDispatching(bool omitted) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mVSyncState || mVSyncState->omitted == omitted) {
+        return;
+    }
+
+    mVSyncState->omitted = omitted;
+    mCondition.notify_all();
+}
+
 void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
     std::lock_guard<std::mutex> lock(mMutex);
     mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime);
 
     LOG_FATAL_IF(!mVSyncState);
     mVsyncTracer = (mVsyncTracer + 1) % 2;
-    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, wakeupTime, ++mVSyncState->count,
-                                       vsyncTime, readyTime));
+    mPendingEvents.push_back(makeVSync(mVsyncSchedule->getPhysicalDisplayId(), wakeupTime,
+                                       ++mVSyncState->count, vsyncTime, readyTime));
     mCondition.notify_all();
 }
 
@@ -472,6 +497,21 @@
     mCondition.notify_all();
 }
 
+void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    mPendingEvents.push_back(makeModeRejection(displayId, modeId));
+    mCondition.notify_all();
+}
+
+// Merge lists of buffer stuffed Uids
+void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    for (auto& [uid, count] : bufferStuffedUids) {
+        mBufferStuffedUids.emplace_or_replace(uid, count);
+    }
+}
+
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
@@ -486,9 +526,9 @@
             if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
                 if (event->hotplug.connectionError == 0) {
                     if (event->hotplug.connected && !mVSyncState) {
-                        mVSyncState.emplace(event->header.displayId);
-                    } else if (!event->hotplug.connected && mVSyncState &&
-                               mVSyncState->displayId == event->header.displayId) {
+                        mVSyncState.emplace();
+                    } else if (!event->hotplug.connected &&
+                               mVsyncSchedule->getPhysicalDisplayId() == event->header.displayId) {
                         mVSyncState.reset();
                     }
                 } else {
@@ -521,7 +561,17 @@
         }
 
         if (mVSyncState && vsyncRequested) {
-            mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+            const bool vsyncOmitted =
+                    FlagManager::getInstance().no_vsyncs_on_screen_off() && mVSyncState->omitted;
+            if (vsyncOmitted) {
+                mState = State::Idle;
+                SFTRACE_INT("VsyncPendingScreenOn", 1);
+            } else {
+                mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+                if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+                    SFTRACE_INT("VsyncPendingScreenOn", 0);
+                }
+            }
         } else {
             ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
             mState = State::Idle;
@@ -559,7 +609,7 @@
                 const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
                 const auto deadlineTimestamp = now + timeout.count();
                 const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
-                mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
+                mPendingEvents.push_back(makeVSync(mVsyncSchedule->getPhysicalDisplayId(), now,
                                                    ++mVSyncState->count, expectedVSyncTime,
                                                    deadlineTimestamp));
             }
@@ -701,6 +751,10 @@
 
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                 const DisplayEventConsumers& consumers) {
+    // List of Uids that have been sent vsync data with queued buffer count.
+    // Used to keep track of which Uids can be removed from the map of
+    // buffer stuffed clients.
+    ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers;
     for (const auto& consumer : consumers) {
         DisplayEventReceiver::Event copy = event;
         if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
@@ -710,6 +764,13 @@
                                   event.vsync.vsyncData.preferredExpectedPresentationTime(),
                                   event.vsync.vsyncData.preferredDeadlineTimestamp());
         }
+        auto it = mBufferStuffedUids.find(consumer->mOwnerUid);
+        if (it != mBufferStuffedUids.end()) {
+            copy.vsync.vsyncData.numberQueuedBuffers = it->second;
+            uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid);
+        } else {
+            copy.vsync.vsyncData.numberQueuedBuffers = 0;
+        }
         switch (consumer->postEvent(copy)) {
             case NO_ERROR:
                 break;
@@ -725,6 +786,12 @@
                 removeDisplayEventConnectionLocked(consumer);
         }
     }
+    // The clients that have already received the queued buffer count
+    // can be removed from the buffer stuffed Uid list to avoid
+    // being sent duplicate messages.
+    for (auto uid : uidsPostedQueuedBuffers) {
+        mBufferStuffedUids.erase(uid);
+    }
     if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
         FlagManager::getInstance().vrr_config()) {
         mLastCommittedVsyncTime =
@@ -739,7 +806,7 @@
     StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
     if (mVSyncState) {
         StringAppendF(&result, "{displayId=%s, count=%u%s}\n",
-                      to_string(mVSyncState->displayId).c_str(), mVSyncState->count,
+                      to_string(mVsyncSchedule->getPhysicalDisplayId()).c_str(), mVSyncState->count,
                       mVSyncState->synthetic ? ", synthetic" : "");
     } else {
         StringAppendF(&result, "none\n");
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index bbe4f9d..18bf416 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -21,6 +21,7 @@
 #include <gui/DisplayEventReceiver.h>
 #include <private/gui/BitTube.h>
 #include <sys/types.h>
+#include <ui/DisplayId.h>
 #include <utils/Errors.h>
 
 #include <scheduler/FrameRateMode.h>
@@ -55,6 +56,7 @@
 // ---------------------------------------------------------------------------
 
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
 
 enum class VSyncRequest {
     None = -2,
@@ -106,6 +108,8 @@
     // Feed clients with fake VSYNC, e.g. while the display is off.
     virtual void enableSyntheticVsync(bool) = 0;
 
+    virtual void omitVsyncDispatching(bool) = 0;
+
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     virtual void onHotplugConnectionError(int32_t connectionError) = 0;
@@ -113,6 +117,9 @@
     // called when SF changes the active mode and apps needs to be notified about the change
     virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
 
+    // called when SF rejects the mode change request
+    virtual void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) = 0;
+
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                              std::vector<FrameRateOverride> overrides) = 0;
@@ -134,6 +141,10 @@
 
     virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
                                      int32_t maxLevel) = 0;
+
+    // An elevated number of queued buffers in the server is detected. This propagates a
+    // flag to Choreographer indicating that buffer stuffing recovery should begin.
+    virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
 };
 
 struct IEventThreadCallback {
@@ -165,12 +176,16 @@
 
     void enableSyntheticVsync(bool) override;
 
+    void omitVsyncDispatching(bool) override;
+
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
     void onHotplugConnectionError(int32_t connectionError) override;
 
     void onModeChanged(const scheduler::FrameRateMode&) override;
 
+    void onModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) override;
+
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
 
@@ -184,6 +199,8 @@
     void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
                              int32_t maxLevel) override;
 
+    void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override;
+
 private:
     friend EventThreadTest;
 
@@ -224,6 +241,10 @@
     scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
+    // All consumers that need to recover from buffer stuffing and the number
+    // of their queued buffers.
+    BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex);
+
     IEventThreadCallback& mCallback;
 
     std::thread mThread;
@@ -235,15 +256,14 @@
 
     // VSYNC state of connected display.
     struct VSyncState {
-        explicit VSyncState(PhysicalDisplayId displayId) : displayId(displayId) {}
-
-        const PhysicalDisplayId displayId;
-
         // Number of VSYNC events since display was connected.
         uint32_t count = 0;
 
         // True if VSYNC should be faked, e.g. when display is off.
         bool synthetic = false;
+
+        // True if VSYNC should not be delivered to apps. Used when the display is off.
+        bool omitted = false;
     };
 
     // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 2b9e88c..d02d149 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -31,7 +31,7 @@
     virtual void onChoreographerAttached() = 0;
     virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
                                              Fps renderRate) = 0;
-    virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+    virtual void onCommitNotComposited() = 0;
     virtual void vrrDisplayIdle(bool idle) = 0;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 64b85c0..630beb0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -280,6 +280,9 @@
                     case Layer::FrameRateCompatibility::Exact:
                         return LayerVoteType::ExplicitExact;
                     case Layer::FrameRateCompatibility::Gte:
+                        if (frameRate.isNoVote()) {
+                            return LayerVoteType::NoVote;
+                        }
                         if (isVrrDevice) {
                             return LayerVoteType::ExplicitGte;
                         } else {
@@ -308,6 +311,15 @@
                 const auto setFrameRateVoteType =
                         info->isVisible() ? voteType : LayerVoteType::NoVote;
 
+                const bool hasSetFrameRateOpinion =
+                        frameRate.isValuelessType() || frameRate.vote.rate.isValid();
+                const bool hasCategoryOpinion =
+                        frameRate.category != FrameRateCategory::NoPreference &&
+                        frameRate.category != FrameRateCategory::Default;
+                const bool hasFrameRateOpinionAboveGameDefault =
+                        hasSetFrameRateOpinion || hasCategoryOpinion;
+                const bool hasFrameRateOpinionArr = frameRate.isValid() && !frameRate.isNoVote();
+
                 if (gameModeFrameRateOverride.isValid()) {
                     info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride});
                     SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride");
@@ -315,7 +327,8 @@
                         trace(*info, gameFrameRateOverrideVoteType,
                               gameModeFrameRateOverride.getIntValue());
                     }
-                } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
+                } else if (hasFrameRateOpinionAboveGameDefault &&
+                           frameRate.isVoteValidForMrr(isVrrDevice)) {
                     info->setLayerVote({setFrameRateVoteType,
                                         isValuelessVote ? 0_Hz : frameRate.vote.rate,
                                         frameRate.vote.seamlessness, frameRate.category});
@@ -331,8 +344,18 @@
                         trace(*info, gameFrameRateOverrideVoteType,
                               gameDefaultFrameRateOverride.getIntValue());
                     }
+                } else if (hasFrameRateOpinionArr && frameRate.isVoteValidForMrr(isVrrDevice)) {
+                    // This allows NoPreference votes on ARR devices after considering the
+                    // gameDefaultFrameRateOverride (above).
+                    info->setLayerVote({setFrameRateVoteType,
+                                        isValuelessVote ? 0_Hz : frameRate.vote.rate,
+                                        frameRate.vote.seamlessness, frameRate.category});
+                    if (CC_UNLIKELY(mTraceEnabled)) {
+                        trace(*info, gameFrameRateOverrideVoteType,
+                              frameRate.vote.rate.getIntValue());
+                    }
                 } else {
-                    if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) {
+                    if (hasFrameRateOpinionArr && !frameRate.isVoteValidForMrr(isVrrDevice)) {
                         SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
                                                "%s %s",
                                                info->getName().c_str(),
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index ff1926e..6e2b943 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -504,7 +504,7 @@
             return FrameRateCompatibility::Exact;
         case ANATIVEWINDOW_FRAME_RATE_MIN:
             return FrameRateCompatibility::Min;
-        case ANATIVEWINDOW_FRAME_RATE_GTE:
+        case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE:
             return FrameRateCompatibility::Gte;
         case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
             return FrameRateCompatibility::NoVote;
@@ -562,7 +562,10 @@
 }
 
 bool LayerInfo::FrameRate::isNoVote() const {
-    return vote.type == FrameRateCompatibility::NoVote;
+    // A desired frame rate greater than or equal to 0 is treated as NoVote.
+    bool isNoVoteGte = FlagManager::getInstance().arr_setframerate_gte_enum() &&
+            vote.type == FrameRateCompatibility::Gte && !vote.rate.isValid();
+    return vote.type == FrameRateCompatibility::NoVote || isNoVoteGte;
 }
 
 bool LayerInfo::FrameRate::isValuelessType() const {
@@ -577,7 +580,12 @@
         case FrameRateCompatibility::Default:
         case FrameRateCompatibility::ExactOrMultiple:
         case FrameRateCompatibility::Exact:
+            return false;
         case FrameRateCompatibility::Gte:
+            if (isNoVote()) {
+                // Special case: GTE 0 is same as NoVote.
+                return true;
+            }
             return false;
     }
 }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index ab9014e..97f1f8f 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -489,6 +489,20 @@
     return mGetRankedFrameRatesCache->result;
 }
 
+using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
+using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
+
+PerUidLayerRequirements groupLayersByUid(
+        const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
+    PerUidLayerRequirements layersByUid;
+    for (const auto& layer : layers) {
+        const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
+        auto& layersWithSameUid = it->second;
+        layersWithSameUid.push_back(&layer);
+    }
+    return layersByUid;
+}
+
 auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
                                                     GlobalSignals signals, Fps pacesetterFps) const
         -> RankedFrameRates {
@@ -525,6 +539,43 @@
         return {ranking, GlobalSignals{.powerOnImminent = true}};
     }
 
+    // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
+    // which will touch boost when there are no ExplicitDefault layer votes on the app.
+    // At most one app can have the "HighHint" touch boost vote at a time.
+    // This accounts for cases such as games that use `setFrameRate`
+    // with Default compatibility to limit the frame rate and disabling touch boost.
+    bool isAppTouchBoost = false;
+    const auto layersByUid = groupLayersByUid(layers);
+    for (const auto& [uid, layersWithSameUid] : layersByUid) {
+        bool hasHighHint = false;
+        bool hasExplicitDefault = false;
+        for (const auto& layer : layersWithSameUid) {
+            switch (layer->vote) {
+                case LayerVoteType::ExplicitDefault:
+                    hasExplicitDefault = true;
+                    break;
+                case LayerVoteType::ExplicitCategory:
+                    if (layer->frameRateCategory == FrameRateCategory::HighHint) {
+                        hasHighHint = true;
+                    }
+                    break;
+                default:
+                    // No action
+                    break;
+            }
+            if (hasHighHint && hasExplicitDefault) {
+                break;
+            }
+        }
+
+        if (hasHighHint && !hasExplicitDefault) {
+            // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes
+            // (which prevents touch boost due to games use case).
+            isAppTouchBoost = true;
+            break;
+        }
+    }
+
     int noVoteLayers = 0;
     // Layers that prefer the same mode ("no-op").
     int noPreferenceLayers = 0;
@@ -535,7 +586,6 @@
     int explicitExact = 0;
     int explicitGteLayers = 0;
     int explicitCategoryVoteLayers = 0;
-    int interactiveLayers = 0;
     int seamedFocusedLayers = 0;
     int categorySmoothSwitchOnlyLayers = 0;
 
@@ -563,11 +613,9 @@
                 explicitGteLayers++;
                 break;
             case LayerVoteType::ExplicitCategory:
-                if (layer.frameRateCategory == FrameRateCategory::HighHint) {
-                    // HighHint does not count as an explicit signal from an app. It may be
-                    // be a touch signal.
-                    interactiveLayers++;
-                } else {
+                // HighHint does not count as an explicit signal from an app. It is a touch signal
+                // sent from UI Toolkit.
+                if (layer.frameRateCategory != FrameRateCategory::HighHint) {
                     explicitCategoryVoteLayers++;
                 }
                 if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
@@ -877,14 +925,11 @@
         return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
     };
 
-    // A method for UI Toolkit to send the touch signal via "HighHint" category vote,
-    // which will touch boost when there are no ExplicitDefault layer votes. This is an
-    // incomplete solution but accounts for cases such as games that use `setFrameRate` with default
+    // This accounts for cases such as games that use `setFrameRate` with Default
     // compatibility to limit the frame rate, which should not have touch boost.
-    const bool hasInteraction = signals.touch || interactiveLayers > 0;
-
-    if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() &&
-        isTouchBoostForCategory()) {
+    const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0;
+    const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost;
+    if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) {
         const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
         using fps_approx_ops::operator<;
 
@@ -912,40 +957,6 @@
     return {ranking, kNoSignals};
 }
 
-using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>;
-using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>;
-
-PerUidLayerRequirements groupLayersByUid(
-        const std::vector<RefreshRateSelector::LayerRequirement>& layers) {
-    PerUidLayerRequirements layersByUid;
-    for (const auto& layer : layers) {
-        const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first;
-        auto& layersWithSameUid = it->second;
-        layersWithSameUid.push_back(&layer);
-    }
-
-    // Remove uids that can't have a frame rate override
-    for (auto it = layersByUid.begin(); it != layersByUid.end();) {
-        const auto& layersWithSameUid = it->second;
-        bool skipUid = false;
-        for (const auto& layer : layersWithSameUid) {
-            using LayerVoteType = RefreshRateSelector::LayerVoteType;
-
-            if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) {
-                skipUid = true;
-                break;
-            }
-        }
-        if (skipUid) {
-            it = layersByUid.erase(it);
-        } else {
-            ++it;
-        }
-    }
-
-    return layersByUid;
-}
-
 auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
                                                 Fps displayRefreshRate,
                                                 GlobalSignals globalSignals) const
@@ -990,6 +1001,7 @@
         bool hasExplicitExactOrMultiple = false;
         bool hasExplicitDefault = false;
         bool hasHighHint = false;
+        bool hasSkipOverrideLayer = false;
         for (const auto& layer : layersWithSameUid) {
             switch (layer->vote) {
                 case LayerVoteType::ExplicitExactOrMultiple:
@@ -1003,15 +1015,25 @@
                         hasHighHint = true;
                     }
                     break;
+                case LayerVoteType::Max:
+                case LayerVoteType::Heuristic:
+                    hasSkipOverrideLayer = true;
+                    break;
                 default:
                     // No action
                     break;
             }
-            if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) {
+            if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint &&
+                hasSkipOverrideLayer) {
                 break;
             }
         }
 
+        if (hasSkipOverrideLayer) {
+            ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid);
+            continue;
+        }
+
         // Layers with ExplicitExactOrMultiple expect touch boost
         if (globalSignals.touch && hasExplicitExactOrMultiple) {
             continue;
@@ -1510,6 +1532,9 @@
 
     mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
     mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
+    mAllFrameRates = filterRefreshRates(FpsRanges(getSupportedFrameRateRangeLocked(),
+                                                  getSupportedFrameRateRangeLocked()),
+                                        "full frame rates");
 }
 
 bool RefreshRateSelector::isVrrDevice() const {
@@ -1535,6 +1560,27 @@
     return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
 }
 
+std::vector<float> RefreshRateSelector::getSupportedFrameRates() const {
+    std::scoped_lock lock(mLock);
+    // TODO(b/356986687) Remove the limit once we have the anchor list implementation.
+    const size_t frameRatesSize = std::min<size_t>(11, mAllFrameRates.size());
+    std::vector<float> supportedFrameRates;
+    supportedFrameRates.reserve(frameRatesSize);
+    std::transform(mAllFrameRates.rbegin(),
+                   mAllFrameRates.rbegin() + static_cast<int>(frameRatesSize),
+                   std::back_inserter(supportedFrameRates),
+                   [](FrameRateMode mode) { return mode.fps.getValue(); });
+    return supportedFrameRates;
+}
+
+FpsRange RefreshRateSelector::getSupportedFrameRateRangeLocked() const {
+    using fps_approx_ops::operator<;
+    if (mMaxRefreshRateModeIt->second->getPeakFps() < kMinSupportedFrameRate) {
+        return {mMaxRefreshRateModeIt->second->getPeakFps(), kMinSupportedFrameRate};
+    }
+    return {kMinSupportedFrameRate, mMaxRefreshRateModeIt->second->getPeakFps()};
+}
+
 auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
     std::lock_guard lock(mLock);
 
@@ -1638,9 +1684,9 @@
 FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) {
     switch (category) {
         case FrameRateCategory::High:
-            return FpsRange{90_Hz, 120_Hz};
+            return FpsRange{kFrameRateCategoryRateHigh, 120_Hz};
         case FrameRateCategory::Normal:
-            return FpsRange{60_Hz, 120_Hz};
+            return FpsRange{kFrameRateCategoryRateNormal, 120_Hz};
         case FrameRateCategory::Low:
             return FpsRange{48_Hz, 120_Hz};
         case FrameRateCategory::HighHint:
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index a398c01..8e173b1 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -52,6 +52,12 @@
     // The lowest Render Frame Rate that will ever be selected
     static constexpr Fps kMinSupportedFrameRate = 20_Hz;
 
+    // Start range for FrameRateCategory Normal and High.
+    static constexpr Fps kFrameRateCategoryRateHigh = 90_Hz;
+    static constexpr Fps kFrameRateCategoryRateNormal = 60_Hz;
+    static constexpr std::pair<Fps, Fps> kFrameRateCategoryRates = {kFrameRateCategoryRateNormal,
+                                                                    kFrameRateCategoryRateHigh};
+
     class Policy {
         static constexpr int kAllowGroupSwitchingDefault = false;
 
@@ -433,6 +439,10 @@
 
     bool isVrrDevice() const;
 
+    std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; }
+
+    std::vector<float> getSupportedFrameRates() const EXCLUDES(mLock);
+
 private:
     friend struct TestableRefreshRateSelector;
 
@@ -543,6 +553,7 @@
     // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
     std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
     std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
+    std::vector<FrameRateMode> mAllFrameRates GUARDED_BY(mLock);
 
     // Caches whether the device is VRR-compatible based on the active display mode.
     std::atomic_bool mIsVrrDevice = false;
@@ -587,6 +598,9 @@
     // Used to detect (lack of) frame activity.
     ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
     std::atomic<bool> mIdleTimerStarted = false;
+
+    // Returns the range of supported frame rates.
+    FpsRange getSupportedFrameRateRangeLocked() const REQUIRES(mLock);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5e13154..4da76f6 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -203,12 +203,16 @@
 
 void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
                               TimePoint expectedVsyncTime) {
+    const auto debugPresentDelay = mDebugPresentDelay.load();
+    mDebugPresentDelay.store(std::nullopt);
+
     const FrameTargeter::BeginFrameArgs beginFrameArgs =
             {.frameBeginTime = SchedulerClock::now(),
              .vsyncId = vsyncId,
              .expectedVsyncTime = expectedVsyncTime,
              .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration,
-             .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration};
+             .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration,
+             .debugPresentTimeDelay = debugPresentDelay};
 
     ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked();
     pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr);
@@ -238,7 +242,7 @@
             if (FlagManager::getInstance().vrr_config()) {
                 compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId);
             }
-            mSchedulerCallback.onCommitNotComposited(pacesetterPtr->displayId);
+            mSchedulerCallback.onCommitNotComposited();
             return;
         }
     }
@@ -405,6 +409,14 @@
     eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
 }
 
+void Scheduler::omitVsyncDispatching(bool omitted) {
+    eventThreadFor(Cycle::Render).omitVsyncDispatching(omitted);
+    // Note: If we don't couple Cycle::LastComposite event thread, there is a black screen
+    // after boot. This is most likely sysui or system_server dependency on sf instance
+    // Choreographer
+    eventThreadFor(Cycle::LastComposite).omitVsyncDispatching(omitted);
+}
+
 void Scheduler::onFrameRateOverridesChanged() {
     const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
         std::scoped_lock lock(mDisplayLock);
@@ -426,7 +438,10 @@
     eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
 }
 
-bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode,
+                                     bool clearContentRequirements) {
     const bool isPacesetter =
             FTL_FAKE_GUARD(kMainThreadContext,
                            (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
@@ -435,9 +450,11 @@
         std::lock_guard<std::mutex> lock(mPolicyLock);
         mPolicy.emittedModeOpt = mode;
 
-        // Invalidate content based refresh rate selection so it could be calculated
-        // again for the new refresh rate.
-        mPolicy.contentRequirements.clear();
+        if (clearContentRequirements) {
+            // Invalidate content based refresh rate selection so it could be calculated
+            // again for the new refresh rate.
+            mPolicy.contentRequirements.clear();
+        }
     }
 
     if (hasEventThreads()) {
@@ -446,6 +463,13 @@
 
     return isPacesetter;
 }
+#pragma clang diagnostic pop
+
+void Scheduler::onDisplayModeRejected(PhysicalDisplayId displayId, DisplayModeId modeId) {
+    if (hasEventThreads()) {
+        eventThreadFor(Cycle::Render).onModeRejected(displayId, modeId);
+    }
+}
 
 void Scheduler::emitModeChangeIfNeeded() {
     if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
@@ -483,6 +507,8 @@
     }
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
 void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refreshRate) {
     const bool isPacesetter =
             FTL_FAKE_GUARD(kMainThreadContext,
@@ -494,6 +520,7 @@
     setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
                    refreshRate.getPeriod());
 }
+#pragma clang diagnostic pop
 
 void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
     mRefreshRateStats->setPowerMode(powerMode);
@@ -909,6 +936,8 @@
     }
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
 void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
     const bool changed = (std::scoped_lock(mPolicyLock),
                           updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
@@ -917,6 +946,7 @@
         onFrameRateOverridesChanged();
     }
 }
+#pragma clang diagnostic pop
 
 bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
                                                Fps displayRefreshRate) {
@@ -931,6 +961,11 @@
     return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
 }
 
+void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) {
+    if (!mRenderEventThread) return;
+    mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids));
+}
+
 void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
     std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c88b563..a2cdd46 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,13 +36,14 @@
 #include <ftl/non_null.h>
 #include <ftl/optional.h>
 #include <scheduler/Features.h>
+#include <scheduler/FrameRateMode.h>
 #include <scheduler/FrameTargeter.h>
 #include <scheduler/Time.h>
 #include <scheduler/VsyncConfig.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMap.h>
 
-#include "Display/DisplayModeRequest.h"
+#include "DisplayHardware/DisplayMode.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
 #include "ISchedulerCallback.h"
@@ -151,9 +152,13 @@
     void dispatchHotplugError(int32_t errorCode);
 
     // Returns true if the PhysicalDisplayId is the pacesetter.
-    bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
+    bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&,
+                              bool clearContentRequirements) EXCLUDES(mPolicyLock);
+
+    void onDisplayModeRejected(PhysicalDisplayId, DisplayModeId);
 
     void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
+    void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
 
     void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
 
@@ -204,6 +209,7 @@
         ftl::FakeGuard guard(kMainThreadContext);
         resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
     }
+    void resync() override EXCLUDES(mDisplayLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
     // Passes a vsync sample to VsyncController. Returns true if
@@ -332,6 +338,12 @@
         mPacesetterFrameDurationFractionToSkip = frameDurationFraction;
     }
 
+    // Propagates a flag to the EventThread indicating that buffer stuffing
+    // recovery should begin.
+    void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids);
+
+    void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; }
+
 private:
     friend class TestableScheduler;
 
@@ -459,7 +471,6 @@
     bool throttleVsync(TimePoint, uid_t) override;
     // Get frame interval
     Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
-    void resync() override EXCLUDES(mDisplayLock);
     void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
 
     std::unique_ptr<EventThread> mRenderEventThread;
@@ -597,6 +608,8 @@
 
     FrameRateOverrideMappings mFrameRateOverrideMappings;
     SmallAreaDetectionAllowMappings mSmallAreaDetectionAllowMappings;
+
+    std::atomic<std::optional<TimePoint>> mDebugPresentDelay;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 6e36f02..ff360b7 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -458,7 +458,8 @@
 
 Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
                                                       TimePoint lastConfirmedPresentTime) {
-    SFTRACE_CALL();
+    SFTRACE_FORMAT("%s mNumVsyncsForFrame=%d mPastExpectedPresentTimes.size()=%zu", __func__,
+                   mNumVsyncsForFrame, mPastExpectedPresentTimes.size());
 
     if (mNumVsyncsForFrame <= 1) {
         return 0ns;
@@ -470,12 +471,8 @@
 
     auto prev = lastConfirmedPresentTime.ns();
     for (auto& current : mPastExpectedPresentTimes) {
-        if (CC_UNLIKELY(mTraceOn)) {
-            SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
-                                   static_cast<float>(current.ns() -
-                                                      lastConfirmedPresentTime.ns()) /
-                                           1e6f);
-        }
+        SFTRACE_FORMAT_INSTANT("current %.2f past last signaled fence",
+                               static_cast<float>(current.ns() - prev) / 1e6f);
 
         const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
         if (minPeriodViolation) {
@@ -522,11 +519,9 @@
         const auto front = mPastExpectedPresentTimes.front().ns();
         const bool frontIsBeforeConfirmed = front < lastConfirmedPresentTime.ns() + threshold;
         if (frontIsBeforeConfirmed) {
-            if (CC_UNLIKELY(mTraceOn)) {
-                SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
-                                       static_cast<float>(lastConfirmedPresentTime.ns() - front) /
-                                               1e6f);
-            }
+            SFTRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence",
+                                   static_cast<float>(lastConfirmedPresentTime.ns() - front) /
+                                           1e6f);
             mPastExpectedPresentTimes.pop_front();
         } else {
             break;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 881d678..e63cbb2 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -112,6 +112,8 @@
 
     bool getPendingHardwareVsyncState() const REQUIRES(kMainThreadContext);
 
+    PhysicalDisplayId getPhysicalDisplayId() const { return mId; }
+
 protected:
     using ControllerPtr = std::unique_ptr<VsyncController>;
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 2185bb0..813d4de 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -53,6 +53,8 @@
 
     TimePoint expectedPresentTime() const { return mExpectedPresentTime; }
 
+    std::optional<TimePoint> debugPresentDelay() const { return mDebugPresentTimeDelay; }
+
     std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
 
     // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
@@ -84,6 +86,7 @@
     TimePoint mFrameBeginTime;
     TimePoint mExpectedPresentTime;
     std::optional<TimePoint> mEarliestPresentTime;
+    std::optional<TimePoint> mDebugPresentTimeDelay;
 
     TracedOrdinal<bool> mFramePending;
     TracedOrdinal<bool> mFrameMissed;
@@ -135,6 +138,7 @@
         TimePoint expectedVsyncTime;
         Duration sfWorkDuration;
         Duration hwcMinWorkDuration;
+        std::optional<TimePoint> debugPresentTimeDelay; // used to introduce jank for testing
     };
 
     void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 3ee1e54..5019949 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -31,6 +31,7 @@
 
 std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
 FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+    SFTRACE_CALL();
     if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
         const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
         return {true, mPresentFencesLegacy[i]};
@@ -40,17 +41,28 @@
     auto expectedPresentTime = mExpectedPresentTime;
     for (size_t i = mPresentFences.size(); i != 0; --i) {
         const auto& fence = mPresentFences[i - 1];
+        SFTRACE_FORMAT_INSTANT("fence at idx: %zu expectedPresentTime in %.2f", i - 1,
+                               ticks<std::milli, float>(fence.expectedPresentTime -
+                                                        TimePoint::now()));
 
         if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+            SFTRACE_FORMAT_INSTANT("would not backpressure");
             wouldBackpressure = false;
         }
 
         if (fence.expectedPresentTime <= mFrameBeginTime) {
+            SFTRACE_FORMAT_INSTANT("fence at idx: %zu is %.2f before frame begin "
+                                   "(wouldBackpressure=%s)",
+                                   i - 1,
+                                   ticks<std::milli, float>(mFrameBeginTime -
+                                                            fence.expectedPresentTime),
+                                   wouldBackpressure ? "true" : "false");
             return {wouldBackpressure, fence};
         }
 
         expectedPresentTime = fence.expectedPresentTime;
     }
+    SFTRACE_FORMAT_INSTANT("No fence found");
     return {wouldBackpressure, PresentFence{}};
 }
 
@@ -86,6 +98,7 @@
                                IsFencePendingFuncPtr isFencePendingFuncPtr) {
     mVsyncId = args.vsyncId;
     mFrameBeginTime = args.frameBeginTime;
+    mDebugPresentTimeDelay = args.debugPresentTimeDelay;
 
     // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in
     // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust
@@ -153,6 +166,12 @@
         if (pastPresentTime < 0) return false;
         mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
                                   .expectedPresentTime = fence.expectedPresentTime};
+        SFTRACE_FORMAT_INSTANT("LastSignaledFrameTime expectedPresentTime %.2f ago, signalTime "
+                               "%.2f ago",
+                               ticks<std::milli, float>(mLastSignaledFrameTime.expectedPresentTime -
+                                                        TimePoint::now()),
+                               ticks<std::milli, float>(mLastSignaledFrameTime.signalTime -
+                                                        TimePoint::now()));
         const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
         return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
     }();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 23a2f9f..5947a43 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -64,11 +64,13 @@
 #include <ftl/concat.h>
 #include <ftl/fake_guard.h>
 #include <ftl/future.h>
+#include <ftl/small_map.h>
 #include <ftl/unit.h>
 #include <gui/AidlUtil.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/IProducerListener.h>
+#include <gui/JankInfo.h>
 #include <gui/LayerMetadata.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
@@ -83,6 +85,7 @@
 #include <renderengine/RenderEngine.h>
 #include <renderengine/impl/ExternalTexture.h>
 #include <scheduler/FrameTargeter.h>
+#include <statslog_surfaceflinger.h>
 #include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
@@ -91,6 +94,7 @@
 #include <ui/DisplayStatInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/DynamicDisplayInfo.h>
+#include <ui/FrameRateCategoryRate.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/HdrRenderTypeUtils.h>
 #include <ui/LayerStack.h>
@@ -122,6 +126,7 @@
 #include <gui/SchedulingPolicy.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <ui/DisplayIdentification.h>
+#include "ActivePictureUpdater.h"
 #include "BackgroundExecutor.h"
 #include "Client.h"
 #include "ClientCache.h"
@@ -131,7 +136,6 @@
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
 #include "Effects/Daltonizer.h"
@@ -151,6 +155,7 @@
 #include "LayerVector.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 #include "RegionSamplingThread.h"
 #include "RenderAreaBuilder.h"
 #include "Scheduler/EventThread.h"
@@ -175,6 +180,7 @@
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OutputType.h>
 #include <aidl/android/hardware/graphics/composer3/RenderIntent.h>
 
 #undef NO_THREAD_SAFETY_ANALYSIS
@@ -376,6 +382,7 @@
 const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS");
+const String16 sObservePictureProfiles("android.permission.OBSERVE_PICTURE_PROFILES");
 const String16 sDump("android.permission.DUMP");
 const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
 const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
@@ -432,7 +439,11 @@
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
         mInternalDisplayDensity(
                 getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
-        mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
+        mPowerAdvisor(std::make_unique<
+                      adpf::impl::PowerAdvisor>([this] { disableExpensiveRendering(); },
+                                                std::chrono::milliseconds(
+                                                        sysprop::display_update_imminent_timeout_ms(
+                                                                80)))),
         mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()),
         mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -647,13 +658,14 @@
     }
 }
 
-VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution,
-                                                       ui::PixelFormat format,
+VirtualDisplayId SurfaceFlinger::acquireVirtualDisplay(ui::Size resolution, ui::PixelFormat format,
+                                                       const std::string& uniqueId,
                                                        bool canAllocateHwcForVDS) {
     auto& generator = mVirtualDisplayIdGenerators.hal;
     if (canAllocateHwcForVDS && generator) {
         if (const auto id = generator->generateId()) {
             if (getHwComposer().allocateVirtualDisplay(*id, resolution, &format)) {
+                acquireVirtualDisplaySnapshot(*id, uniqueId);
                 return *id;
             }
 
@@ -667,6 +679,7 @@
 
     const auto id = mVirtualDisplayIdGenerators.gpu.generateId();
     LOG_ALWAYS_FATAL_IF(!id, "Failed to generate ID for GPU virtual display");
+    acquireVirtualDisplaySnapshot(*id, uniqueId);
     return *id;
 }
 
@@ -674,6 +687,7 @@
     if (const auto id = HalVirtualDisplayId::tryCast(displayId)) {
         if (auto& generator = mVirtualDisplayIdGenerators.hal) {
             generator->releaseId(*id);
+            releaseVirtualDisplaySnapshot(*id);
         }
         return;
     }
@@ -681,6 +695,14 @@
     const auto id = GpuVirtualDisplayId::tryCast(displayId);
     LOG_ALWAYS_FATAL_IF(!id);
     mVirtualDisplayIdGenerators.gpu.releaseId(*id);
+    releaseVirtualDisplaySnapshot(*id);
+}
+
+void SurfaceFlinger::releaseVirtualDisplaySnapshot(VirtualDisplayId displayId) {
+    std::lock_guard lock(mVirtualDisplaysMutex);
+    if (!mVirtualDisplays.erase(displayId)) {
+        ALOGW("%s: Virtual display snapshot was not removed", __func__);
+    }
 }
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
@@ -797,6 +819,12 @@
     }));
 }
 
+bool shouldUseGraphiteIfCompiledAndSupported() {
+    return FlagManager::getInstance().graphite_renderengine() ||
+            (FlagManager::getInstance().graphite_renderengine_preview_rollout() &&
+             base::GetBoolProperty(PROPERTY_DEBUG_RENDERENGINE_GRAPHITE_PREVIEW_OPTIN, false));
+}
+
 void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) {
     char prop[PROPERTY_VALUE_MAX];
     property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
@@ -825,14 +853,13 @@
 // is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
 #if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
         COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
-        const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
+        const bool useGraphite = shouldUseGraphiteIfCompiledAndSupported() &&
                 renderengine::RenderEngine::canSupport(kVulkan);
 #else
         const bool useGraphite = false;
-        if (FlagManager::getInstance().graphite_renderengine()) {
-            ALOGE("RenderEngine's Graphite Skia backend was requested with the "
-                  "debug.renderengine.graphite system property, but it is not compiled in this "
-                  "build! Falling back to Ganesh backend selection logic.");
+        if (shouldUseGraphiteIfCompiledAndSupported()) {
+            ALOGE("RenderEngine's Graphite Skia backend was requested, but it is not compiled in "
+                  "this build! Falling back to Ganesh backend selection logic.");
         }
 #endif
         const bool useVulkan = useGraphite ||
@@ -860,6 +887,9 @@
     } else if (algorithm == "kawase2") {
         return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
     } else {
+        if (FlagManager::getInstance().window_blur_kawase2()) {
+            return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
+        }
         return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
     }
 }
@@ -956,16 +986,20 @@
                         }));
                     }));
 
-    mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
-        auto snapshot = perfetto::protos::LayersSnapshotProto{};
-        mScheduler
-                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
-                    snapshot = takeLayersSnapshotProto(traceFlags, TimePoint::now(),
-                                                       mLastCommittedVsyncId, true);
-                })
-                .wait();
-        return snapshot;
-    });
+    mLayerTracing.setTakeLayersSnapshotProtoFunction(
+            [&](uint32_t traceFlags,
+                const LayerTracing::OnLayersSnapshotCallback& onLayersSnapshot) {
+                // Do not wait the future to avoid deadlocks
+                // between main and Perfetto threads (b/313130597)
+                static_cast<void>(mScheduler->schedule(
+                        [&, traceFlags, onLayersSnapshot]() FTL_FAKE_GUARD(mStateLock)
+                                FTL_FAKE_GUARD(kMainThreadContext) {
+                                    auto snapshot =
+                                            takeLayersSnapshotProto(traceFlags, TimePoint::now(),
+                                                                    mLastCommittedVsyncId, true);
+                                    onLayersSnapshot(std::move(snapshot));
+                                }));
+            });
 
     // Commit secondary display(s).
     processDisplayChangesLocked();
@@ -1014,7 +1048,8 @@
             config.cacheUltraHDR =
                     base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
             config.cacheEdgeExtension =
-                    base::GetBoolProperty("debug.sf.edge_extension_shader"s, true);
+                    base::GetBoolProperty("debug.sf.prime_shader_cache.edge_extension_shader"s,
+                                          true);
             return getRenderEngine().primeCache(config);
         });
 
@@ -1221,6 +1256,13 @@
     const auto mode = display->refreshRateSelector().getActiveMode();
     info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
     info->renderFrameRate = mode.fps.getValue();
+    info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config();
+
+    const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates();
+    ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue());
+    info->frameRateCategoryRate = frameRateCategoryRate;
+
+    info->supportedRefreshRates = display->refreshRateSelector().getSupportedFrameRates();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
     info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
@@ -1352,7 +1394,8 @@
             mScheduler->updatePhaseConfiguration(displayId, mode.fps);
 
             if (emitEvent) {
-                mScheduler->onDisplayModeChanged(displayId, mode);
+                mScheduler->onDisplayModeChanged(displayId, mode,
+                                                 /*clearContentRequirements*/ false);
             }
             break;
         case DesiredModeAction::None:
@@ -1411,8 +1454,6 @@
     return future.get();
 }
 
-// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
-[[clang::no_thread_safety_analysis]]
 void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
     SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
 
@@ -1428,8 +1469,6 @@
     if (const auto oldResolution =
                 mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
         oldResolution != activeMode.modePtr->getResolution()) {
-        ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display());
-
         auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
@@ -1447,7 +1486,7 @@
     mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
 
     if (pendingModeOpt->emitEvent) {
-        mScheduler->onDisplayModeChanged(displayId, activeMode);
+        mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
     }
 }
 
@@ -1518,8 +1557,15 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
-                                                       constraints, outTimeline)) {
+        const auto error =
+                mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+                                                          constraints, outTimeline);
+        if (error != display::DisplayModeController::ModeChangeResult::Changed) {
+            dropModeRequest(displayId);
+            if (FlagManager::getInstance().display_config_error_hal() &&
+                error == display::DisplayModeController::ModeChangeResult::Rejected) {
+                mScheduler->onDisplayModeRejected(displayId, desiredModeId);
+            }
             continue;
         }
 
@@ -1660,6 +1706,25 @@
         outProperties->combinations.emplace_back(outCombination);
     }
     outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
+    if (aidlProperties.lutProperties) {
+        std::vector<gui::LutProperties> outLutProperties;
+        for (auto properties : *aidlProperties.lutProperties) {
+            if (!properties) {
+                gui::LutProperties currentProperties;
+                currentProperties.dimension =
+                        static_cast<gui::LutProperties::Dimension>(properties->dimension);
+                currentProperties.size = properties->size;
+                currentProperties.samplingKeys.reserve(properties->samplingKeys.size());
+                std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(),
+                               std::back_inserter(currentProperties.samplingKeys),
+                               [](const auto& val) {
+                                   return static_cast<gui::LutProperties::SamplingKey>(val);
+                               });
+                outLutProperties.push_back(std::move(currentProperties));
+            }
+        }
+        outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end());
+    }
     return NO_ERROR;
 }
 
@@ -1814,6 +1879,24 @@
     }));
 }
 
+status_t SurfaceFlinger::getMaxLayerPictureProfiles(const sp<IBinder>& displayToken,
+                                                    int32_t* outMaxProfiles) {
+    const char* const whence = __func__;
+    auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
+        const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+        if (index < 0) {
+            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+            return 0;
+        }
+        const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
+        return state.maxLayerPictureProfiles > 0 ? state.maxLayerPictureProfiles
+                : state.hasPictureProcessing     ? 1
+                                                 : 0;
+    });
+    *outMaxProfiles = future.get();
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
                                           const std::vector<ui::Hdr>& hdrTypes) {
     Mutex::Autolock lock(mStateLock);
@@ -2224,12 +2307,23 @@
         return;
     }
 
-    if (FlagManager::getInstance().hotplug2()) {
-        // TODO(b/311403559): use enum type instead of int
+    if (event < DisplayHotplugEvent::ERROR_LINK_UNSTABLE) {
+        // This needs to be kept in sync with DisplayHotplugEvent to prevent passing new errors.
         const auto errorCode = static_cast<int32_t>(event);
-        ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
-        mScheduler->dispatchHotplugError(errorCode);
+        ALOGW("%s: Unknown hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode,
+              hwcDisplayId);
+        return;
     }
+
+    if (event == DisplayHotplugEvent::ERROR_LINK_UNSTABLE &&
+        !FlagManager::getInstance().display_config_error_hal()) {
+        return;
+    }
+
+    // TODO(b/311403559): use enum type instead of int
+    const auto errorCode = static_cast<int32_t>(event);
+    ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
+    mScheduler->dispatchHotplugError(errorCode);
 }
 
 void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
@@ -2511,17 +2605,13 @@
         frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence);
         gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported;
         mLayersWithQueuedFrames.emplace(it->second, gameMode);
-        mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
     }
 
     updateLayerHistory(latchTime);
     mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) {
-        // update output dirty region if we have a queued buffer that is visible or a snapshot
-        // recently became invisible
-        // TODO(b/360050020) investigate if we need to update dirty region when layer color changes
-        if ((snapshot.isVisible &&
-             (mLayersIdsWithQueuedFrames.find(snapshot.path.id) !=
-              mLayersIdsWithQueuedFrames.end())) ||
+        // update output's dirty region if a snapshot is visible and its
+        // content is dirty or if a snapshot recently became invisible
+        if ((snapshot.isVisible && snapshot.contentDirty) ||
             (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) {
             Region visibleReg;
             visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
@@ -2534,7 +2624,7 @@
     }
 
     {
-        SFTRACE_NAME("LLM:commitChanges");
+        SFTRACE_NAME("LayerLifecycleManager:commitChanges");
         mLayerLifecycleManager.commitChanges();
     }
 
@@ -2575,7 +2665,7 @@
     }
 
     {
-        ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display());
+        Mutex::Autolock lock(mStateLock);
 
         for (const auto [displayId, _] : frameTargets) {
             if (mDisplayModeController.isModeSetPending(displayId)) {
@@ -2678,13 +2768,6 @@
         mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(),
                                                 updateAttachedChoreographer);
 
-        if (FlagManager::getInstance().connected_display()) {
-            initiateDisplayModeChanges();
-        }
-    }
-
-    if (!FlagManager::getInstance().connected_display()) {
-        ftl::FakeGuard guard(mStateLock);
         initiateDisplayModeChanges();
     }
 
@@ -2747,16 +2830,6 @@
             compositionengine::Feature::kSnapshotLayerMetadata);
 
     refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
-
-    if (!FlagManager::getInstance().ce_fence_promise()) {
-        refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (auto& [layer, _] : mLayersWithQueuedFrames) {
-            if (const auto& layerFE = layer->getCompositionEngineLayerFE(
-                        {static_cast<uint32_t>(layer->sequence)}))
-                refreshArgs.layersWithQueuedFrames.push_back(layerFE);
-        }
-    }
-
     refreshArgs.outputColorSetting = mDisplayColorSetting;
     refreshArgs.forceOutputColorMode = mForceColorMode;
 
@@ -2820,51 +2893,38 @@
         layer->onPreComposition(refreshArgs.refreshStartTime);
     }
 
-    if (FlagManager::getInstance().ce_fence_promise()) {
-        for (auto& [layer, layerFE] : layers) {
-            attachReleaseFenceFutureToLayer(layer, layerFE,
-                                            layerFE->mSnapshot->outputFilter.layerStack);
-        }
+    for (auto& [layer, layerFE] : layers) {
+        attachReleaseFenceFutureToLayer(layer, layerFE,
+                                        layerFE->mSnapshot->outputFilter.layerStack);
+    }
 
-        refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
-        for (auto& [layer, _] : mLayersWithQueuedFrames) {
-            if (const auto& layerFE = layer->getCompositionEngineLayerFE(
-                        {static_cast<uint32_t>(layer->sequence)})) {
-                refreshArgs.layersWithQueuedFrames.push_back(layerFE);
-                // Some layers are not displayed and do not yet have a future release fence
-                if (layerFE->getReleaseFencePromiseStatus() ==
-                            LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
-                    layerFE->getReleaseFencePromiseStatus() ==
-                            LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
-                    // layerStack is invalid because layer is not on a display
-                    attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
-                                                    ui::INVALID_LAYER_STACK);
-                }
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (auto& [layer, _] : mLayersWithQueuedFrames) {
+        if (const auto& layerFE =
+                    layer->getCompositionEngineLayerFE({static_cast<uint32_t>(layer->sequence)})) {
+            refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+            // Some layers are not displayed and do not yet have a future release fence
+            if (layerFE->getReleaseFencePromiseStatus() ==
+                        LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
+                layerFE->getReleaseFencePromiseStatus() ==
+                        LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
+                // layerStack is invalid because layer is not on a display
+                attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
+                                                ui::INVALID_LAYER_STACK);
             }
         }
+    }
 
-        mCompositionEngine->present(refreshArgs);
-        moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+    mCompositionEngine->present(refreshArgs);
+    moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 
-        for (auto& [layer, layerFE] : layers) {
-            CompositionResult compositionResult{layerFE->stealCompositionResult()};
-            if (compositionResult.lastClientCompositionFence) {
-                layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
-            }
+    for (auto& [layer, layerFE] : layers) {
+        CompositionResult compositionResult{layerFE->stealCompositionResult()};
+        if (compositionResult.lastClientCompositionFence) {
+            layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
         }
-
-    } else {
-        mCompositionEngine->present(refreshArgs);
-        moveSnapshotsFromCompositionArgs(refreshArgs, layers);
-
-        for (auto [layer, layerFE] : layers) {
-            CompositionResult compositionResult{layerFE->stealCompositionResult()};
-            for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
-                layer->onLayerDisplayed(std::move(releaseFence), layerStack);
-            }
-            if (compositionResult.lastClientCompositionFence) {
-                layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
-            }
+        if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+            mActivePictureUpdater.onLayerComposed(*layer, *layerFE, compositionResult);
         }
     }
 
@@ -2937,7 +2997,6 @@
     mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
 
     mLayersWithQueuedFrames.clear();
-    mLayersIdsWithQueuedFrames.clear();
     doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(),
                                   vsyncId);
 
@@ -3077,12 +3136,40 @@
 
     const TimePoint presentTime = TimePoint::now();
 
+    // The Uids of layer owners that are in buffer stuffing mode, and their elevated
+    // buffer counts. Messages to start recovery are sent exclusively to these Uids.
+    BufferStuffingMap bufferStuffedUids;
+
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
     mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime,
                                  pacesetterGpuCompositionDoneFenceTime);
 
+    // Find and register any layers that are in buffer stuffing mode
+    const auto& presentFrames = mFrameTimeline->getPresentFrames();
+
+    for (const auto& frame : presentFrames) {
+        const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId());
+        if (!layer) continue;
+        uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0;
+        int32_t jankType = frame->getJankType().value_or(JankType::None);
+        if (jankType & JankType::BufferStuffing &&
+            layer->flags & layer_state_t::eRecoverableFromBufferStuffing) {
+            auto [it, wasEmplaced] =
+                    bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers);
+            // Update with maximum number of queued buffers, allows clients drawing
+            // multiple windows to account for the most severely stuffed window
+            if (!wasEmplaced && it->second < numberQueuedBuffers) {
+                it->second = numberQueuedBuffers;
+            }
+        }
+    }
+
+    if (!bufferStuffedUids.empty()) {
+        mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids));
+    }
+
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
     // but that should be okay since CompositorTiming has snapping logic.
@@ -3094,12 +3181,12 @@
 
     const auto schedule = mScheduler->getVsyncSchedule();
     const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
-    const Period vsyncPeriod = schedule->period();
+    const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps;
     const nsecs_t vsyncPhase =
             mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset;
 
-    const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
-                                            presentLatency.ns());
+    const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(),
+                                            vsyncPhase, presentLatency.ns());
 
     ui::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
     {
@@ -3119,13 +3206,8 @@
             auto optDisplay = layerStackToDisplay.get(layerStack);
             if (optDisplay && !optDisplay->get()->isVirtual()) {
                 auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
-                                                   ui::INVALID_LAYER_STACK);
-                } else {
-                    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
-                                            ui::INVALID_LAYER_STACK);
-                }
+                layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
+                                               ui::INVALID_LAYER_STACK);
             }
         }
         layer->releasePendingBuffer(presentTime.ns());
@@ -3139,9 +3221,23 @@
         layer->releasePendingBuffer(presentTime.ns());
     }
 
+    for (const auto& layerEvent : mLayerEvents) {
+        auto result =
+                stats::stats_write(stats::SURFACE_CONTROL_EVENT,
+                                   static_cast<int32_t>(layerEvent.uid),
+                                   static_cast<int64_t>(layerEvent.timeSinceLastEvent.count()),
+                                   static_cast<int32_t>(layerEvent.dataspace));
+        if (result < 0) {
+            ALOGW("Failed to report layer event with error: %d", result);
+        }
+    }
+    mLayerEvents.clear();
+
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
             hdrInfoListeners;
-    bool haveNewListeners = false;
+    bool haveNewHdrInfoListeners = false;
+    sp<gui::IActivePictureListener> activePictureListener;
+    bool haveNewActivePictureListener = false;
     {
         Mutex::Autolock lock(mStateLock);
         if (mFpsReporter) {
@@ -3151,6 +3247,7 @@
         if (mTunnelModeEnabledReporter) {
             mTunnelModeEnabledReporter->updateTunnelModeStatus();
         }
+
         hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
         for (const auto& [displayId, reporter] : mHdrLayerInfoListeners) {
             if (reporter && reporter->hasListeners()) {
@@ -3159,11 +3256,15 @@
                 }
             }
         }
-        haveNewListeners = mAddingHDRLayerInfoListener; // grab this with state lock
+        haveNewHdrInfoListeners = mAddingHDRLayerInfoListener; // grab this with state lock
         mAddingHDRLayerInfoListener = false;
+
+        activePictureListener = mActivePictureListener;
+        haveNewActivePictureListener = mHaveNewActivePictureListener;
+        mHaveNewActivePictureListener = false;
     }
 
-    if (haveNewListeners || mHdrLayerInfoChanged) {
+    if (haveNewHdrInfoListeners || mHdrLayerInfoChanged) {
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
             int32_t maxArea = 0;
@@ -3181,7 +3282,15 @@
                                             snapshot.desiredHdrSdrRatio < 1.f
                                             ? std::numeric_limits<float>::infinity()
                                             : snapshot.desiredHdrSdrRatio;
-                                    info.mergeDesiredRatio(desiredHdrSdrRatio);
+
+                                    float desiredRatio = desiredHdrSdrRatio;
+                                    if (FlagManager::getInstance().begone_bright_hlg() &&
+                                        desiredHdrSdrRatio ==
+                                                std::numeric_limits<float>::infinity()) {
+                                        desiredRatio = getIdealizedMaxHeadroom(snapshot.dataspace);
+                                    }
+
+                                    info.mergeDesiredRatio(desiredRatio);
                                     info.numberOfHdrLayers++;
                                     const auto displayFrame = outputLayer->getState().displayFrame;
                                     const int32_t area =
@@ -3213,9 +3322,19 @@
             listener->dispatchHdrLayerInfo(info);
         }
     }
-
     mHdrLayerInfoChanged = false;
 
+    if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        // Track, update and notify changes to active pictures - layers that are undergoing picture
+        // processing
+        if (mActivePictureUpdater.updateAndHasChanged() || haveNewActivePictureListener) {
+            if (activePictureListener) {
+                activePictureListener->onActivePicturesChanged(
+                        mActivePictureUpdater.getActivePictures());
+            }
+        }
+    }
+
     mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
     mTransactionCallbackInvoker.clearCompletedTransactions();
 
@@ -3274,8 +3393,6 @@
         // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
         SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
     }
-
-    logFrameStats(presentTime);
 }
 
 void SurfaceFlinger::commitTransactions() {
@@ -3412,6 +3529,9 @@
     DisplayModes newModes;
     for (const auto& hwcMode : hwcModes) {
         const auto id = nextModeId++;
+        OutputType hdrOutputType = FlagManager::getInstance().connected_display_hdr()
+                ? hwcMode.hdrOutputType
+                : OutputType::INVALID;
         newModes.try_emplace(id,
                              DisplayMode::Builder(hwcMode.hwcId)
                                      .setId(id)
@@ -3422,6 +3542,7 @@
                                      .setDpiX(hwcMode.dpiX)
                                      .setDpiY(hwcMode.dpiY)
                                      .setGroup(hwcMode.configGroup)
+                                     .setHdrOutputType(hdrOutputType)
                                      .build());
     }
 
@@ -3463,10 +3584,8 @@
                         processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
                                               displayString.c_str());
                 if (!activeModeIdOpt) {
-                    if (FlagManager::getInstance().hotplug2()) {
-                        mScheduler->dispatchHotplugError(
-                                static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
-                    }
+                    mScheduler->dispatchHotplugError(
+                            static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
                     getHwComposer().disconnectDisplay(displayId);
                     continue;
                 }
@@ -3557,7 +3676,9 @@
     }
     state.isProtected = true;
     state.displayName = std::move(info.name);
-
+    state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId);
+    state.hasPictureProcessing =
+            getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING);
     mCurrentState.displays.add(token, state);
     ALOGI("Connecting %s", displayString);
     return activeModeId;
@@ -3665,6 +3786,26 @@
     return display;
 }
 
+void SurfaceFlinger::incRefreshableDisplays() {
+    if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+        mRefreshableDisplays++;
+        if (mRefreshableDisplays == 1) {
+            ftl::FakeGuard guard(kMainThreadContext);
+            mScheduler->omitVsyncDispatching(false);
+        }
+    }
+}
+
+void SurfaceFlinger::decRefreshableDisplays() {
+    if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+        mRefreshableDisplays--;
+        if (mRefreshableDisplays == 0) {
+            ftl::FakeGuard guard(kMainThreadContext);
+            mScheduler->omitVsyncDispatching(true);
+        }
+    }
+}
+
 void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
                                          const DisplayDeviceState& state) {
 #ifdef QCOM_UM_FAMILY
@@ -3711,12 +3852,15 @@
     if (const auto& physical = state.physical) {
         builder.setId(physical->id);
     } else {
-        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, canAllocateHwcForVDS));
+        builder.setId(acquireVirtualDisplay(resolution, pixelFormat, state.uniqueId,
+                canAllocateHwcForVDS));
     }
 
     builder.setPixels(resolution);
     builder.setIsSecure(state.isSecure);
     builder.setIsProtected(state.isProtected);
+    builder.setHasPictureProcessing(state.hasPictureProcessing);
+    builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles);
     builder.setPowerAdvisor(mPowerAdvisor.get());
     builder.setName(state.displayName);
     auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -3776,6 +3920,10 @@
         display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
     }
 
+    if (display->isRefreshable()) {
+        incRefreshableDisplays();
+    }
+
     mDisplays.try_emplace(displayToken, std::move(display));
 
     // For an external display, loadDisplayModes already attempted to select the same mode
@@ -3810,6 +3958,10 @@
         } else {
             mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
         }
+
+        if (display->isRefreshable()) {
+            decRefreshableDisplays();
+        }
     }
 
     mDisplays.erase(displayToken);
@@ -3844,6 +3996,10 @@
             if (display->isVirtual()) {
                 releaseVirtualDisplay(display->getVirtualId());
             }
+
+            if (display->isRefreshable()) {
+                decRefreshableDisplays();
+            }
         }
 
         mDisplays.erase(displayToken);
@@ -4008,7 +4164,8 @@
                                                       inputWindowCommands =
                                                               std::move(mInputWindowCommands),
                                                       inputFlinger = mInputFlinger, this,
-                                                      visibleWindowsChanged, vsyncId, frameTime]() {
+                                                      visibleWindowsChanged, vsyncId,
+                                                      frameTime]() mutable {
         SFTRACE_NAME("BackgroundExecutor::updateInputFlinger");
         if (updateWindowInfo) {
             mWindowInfosListenerInvoker
@@ -4287,7 +4444,7 @@
     scheduleNotifyExpectedPresentHint(displayId);
 }
 
-void SurfaceFlinger::onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) {
+void SurfaceFlinger::onCommitNotComposited() {
     if (FlagManager::getInstance().commit_not_composited()) {
         mFrameTimeline->onCommitNotComposited();
     }
@@ -4440,6 +4597,7 @@
     SFTRACE_INT("mTransactionFlags", transactionFlags);
 
     if (const bool scheduled = transactionFlags & mask; !scheduled) {
+        mScheduler->resync();
         scheduleCommit(frameHint);
     } else if (frameHint == FrameHint::kActive) {
         // Even if the next frame is already scheduled, we should reset the idle timer
@@ -4749,6 +4907,7 @@
     for (auto& state : states) {
         resolvedStates.emplace_back(std::move(state));
         auto& resolvedState = resolvedStates.back();
+        resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
         if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
             resolvedState.state.surface) {
             sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
@@ -4760,9 +4919,8 @@
             if (resolvedState.externalTexture) {
                 resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
             }
-            mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
+            mBufferCountTracker.increment(resolvedState.layerId);
         }
-        resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
         if (resolvedState.state.what & layer_state_t::eReparent) {
             resolvedState.parentId =
                     getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
@@ -5205,8 +5363,9 @@
             std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
             if (pendingBufferCounter) {
                 std::string counterName = layer->getPendingBufferCounterName();
-                mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
+                mBufferCountTracker.add(LayerHandle::getLayerId(outResult.handle), counterName,
                                         pendingBufferCounter);
+                args.pendingBuffers = pendingBufferCounter;
             }
         } break;
         default:
@@ -5253,7 +5412,7 @@
     return NO_ERROR;
 }
 
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) {
     {
         // Used to remove stalled transactions which uses an internal lock.
         ftl::FakeGuard guard(kMainThreadContext);
@@ -5266,7 +5425,7 @@
 
     Mutex::Autolock stateLock(mStateLock);
     layer->onHandleDestroyed();
-    mBufferCountTracker.remove(handle);
+    mBufferCountTracker.remove(layerId);
     layer.clear();
     setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded);
 }
@@ -5341,7 +5500,15 @@
                      activeDisplay->isPoweredOn(),
              "Trying to change power mode on inactive display without powering off active display");
 
+    const bool couldRefresh = display->isRefreshable();
     display->setPowerMode(mode);
+    const bool canRefresh = display->isRefreshable();
+
+    if (couldRefresh && !canRefresh) {
+        decRefreshableDisplays();
+    } else if (!couldRefresh && canRefresh) {
+        incRefreshableDisplays();
+    }
 
     const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
     if (currentMode == hal::PowerMode::OFF) {
@@ -5675,6 +5842,14 @@
             utils::Dumper::Section section(dumper,
                                            ftl::Concat("Virtual Display ", displayId.value).str());
             display->dump(dumper);
+
+            if (const auto virtualIdOpt = VirtualDisplayId::tryCast(displayId)) {
+                std::lock_guard lock(mVirtualDisplaysMutex);
+                const auto virtualSnapshotIt = mVirtualDisplays.find(virtualIdOpt.value());
+                if (virtualSnapshotIt != mVirtualDisplays.end()) {
+                    virtualSnapshotIt->second.dump(dumper);
+                }
+            }
         }
     }
 }
@@ -5767,7 +5942,7 @@
 
 void SurfaceFlinger::dumpFrontEnd(std::string& result) {
     std::ostringstream out;
-    out << "\nComposition list\n";
+    out << "\nComposition list (bottom to top)\n";
     ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
     for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
         if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
@@ -5795,7 +5970,7 @@
 
 void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
     std::ostringstream out;
-    out << "\nComposition list\n";
+    out << "\nComposition list (bottom to top)\n";
     ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
     mLayerSnapshotBuilder.forEachVisibleSnapshot(
             [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
@@ -6206,7 +6381,7 @@
     }
     // Numbers from 1000 to 1045 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1045) {
+    if (code >= 1000 && code <= 1046) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -6739,6 +6914,15 @@
                 }
                 return err;
             }
+            // Introduce jank to HWC
+            case 1046: {
+                int32_t jankDelayMs = 0;
+                if (data.readInt32(&jankDelayMs) != NO_ERROR) {
+                    return BAD_VALUE;
+                }
+                mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs)));
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -7182,9 +7366,10 @@
 // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
 // A protected layer has no implication on whether it's secure, which is explicitly set by
 // application to avoid being screenshot or drawn via unsecure display.
-bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const {
+bool SurfaceFlinger::layersHasProtectedLayer(
+        const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
     bool protectedLayerFound = false;
-    for (auto& layerFE : layers) {
+    for (auto& [_, layerFE] : layers) {
         protectedLayerFound |=
                 (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent);
         if (protectedLayerFound) {
@@ -7200,15 +7385,21 @@
 // risk of deadlocks.
 std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread(
         RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
-        std::vector<sp<LayerFE>>& layerFEs) {
+        std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
     return mScheduler
-            ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) {
+            ->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) {
                 SFTRACE_NAME("getSnapshotsFromMainThread");
-                auto layers = getLayerSnapshotsFn();
-                for (auto& [layer, layerFE] : layers) {
-                    attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+                layers = getLayerSnapshotsFn();
+                // Non-threaded RenderEngine eventually returns to the main thread a 2nd time
+                // to complete the screenshot. Release fences should only be added during the 2nd
+                // hop to main thread in order to avoid potential deadlocks from waiting for the
+                // the future fence to fire.
+                if (mRenderEngine->isThreaded()) {
+                    for (auto& [layer, layerFE] : layers) {
+                        attachReleaseFenceFutureToLayer(layer, layerFE.get(),
+                                                        ui::INVALID_LAYER_STACK);
+                    }
                 }
-                layerFEs = extractLayerFEs(layers);
                 return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
             })
             .get();
@@ -7229,80 +7420,41 @@
         return;
     }
 
-    if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
-        std::vector<sp<LayerFE>> layerFEs;
-        auto displayState =
-                getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs);
+    std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+    auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers);
 
-        const bool supportsProtected = getRenderEngine().supportsProtectedContent();
-        bool hasProtectedLayer = false;
-        if (allowProtected && supportsProtected) {
-            hasProtectedLayer = layersHasProtectedLayer(layerFEs);
-        }
-        const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
-        const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
-                GRALLOC_USAGE_HW_TEXTURE |
-                (isProtected ? GRALLOC_USAGE_PROTECTED
-                             : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
-        sp<GraphicBuffer> buffer =
-                getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
-                                                 static_cast<android_pixel_format>(reqPixelFormat),
-                                                 1 /* layerCount */, usage, "screenshot");
-
-        const status_t bufferStatus = buffer->initCheck();
-        if (bufferStatus != OK) {
-            // Animations may end up being really janky, but don't crash here.
-            // Otherwise an irreponsible process may cause an SF crash by allocating
-            // too much.
-            ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
-            invokeScreenCaptureError(bufferStatus, captureListener);
-            return;
-        }
-        const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
-                renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
-                                                     renderengine::impl::ExternalTexture::Usage::
-                                                             WRITEABLE);
-        auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
-                                             grayscale, isProtected, attachGainmap, captureListener,
-                                             displayState, layerFEs);
-        futureFence.get();
-
-    } else {
-        const bool supportsProtected = getRenderEngine().supportsProtectedContent();
-        bool hasProtectedLayer = false;
-        if (allowProtected && supportsProtected) {
-            auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get();
-            hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers));
-        }
-        const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
-        const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
-                GRALLOC_USAGE_HW_TEXTURE |
-                (isProtected ? GRALLOC_USAGE_PROTECTED
-                             : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
-        sp<GraphicBuffer> buffer =
-                getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
-                                                 static_cast<android_pixel_format>(reqPixelFormat),
-                                                 1 /* layerCount */, usage, "screenshot");
-
-        const status_t bufferStatus = buffer->initCheck();
-        if (bufferStatus != OK) {
-            // Animations may end up being really janky, but don't crash here.
-            // Otherwise an irreponsible process may cause an SF crash by allocating
-            // too much.
-            ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
-            invokeScreenCaptureError(bufferStatus, captureListener);
-            return;
-        }
-        const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
-                renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
-                                                     renderengine::impl::ExternalTexture::Usage::
-                                                             WRITEABLE);
-        auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
-                                                   false /* regionSampling */, grayscale,
-                                                   isProtected, attachGainmap, captureListener);
-        futureFence.get();
+    const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+    bool hasProtectedLayer = false;
+    if (allowProtected && supportsProtected) {
+        hasProtectedLayer = layersHasProtectedLayer(layers);
     }
+    const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+    const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+            GRALLOC_USAGE_HW_TEXTURE |
+            (isProtected ? GRALLOC_USAGE_PROTECTED
+                         : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+    sp<GraphicBuffer> buffer =
+            getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+                                             static_cast<android_pixel_format>(reqPixelFormat),
+                                             1 /* layerCount */, usage, "screenshot");
+
+    const status_t bufferStatus = buffer->initCheck();
+    if (bufferStatus != OK) {
+        // Animations may end up being really janky, but don't crash here.
+        // Otherwise an irreponsible process may cause an SF crash by allocating
+        // too much.
+        ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+        invokeScreenCaptureError(bufferStatus, captureListener);
+        return;
+    }
+    const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+            renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+                                                 renderengine::impl::ExternalTexture::Usage::
+                                                         WRITEABLE);
+    auto futureFence =
+            captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
+                              isProtected, attachGainmap, captureListener, displayState, layers);
+    futureFence.get();
 }
 
 std::optional<SurfaceFlinger::OutputCompositionState>
@@ -7341,22 +7493,13 @@
     return std::nullopt;
 }
 
-std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs(
-        const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
-    std::vector<sp<LayerFE>> layerFEs;
-    layerFEs.reserve(layers.size());
-    for (const auto& [_, layerFE] : layers) {
-        layerFEs.push_back(layerFE);
-    }
-    return layerFEs;
-}
-
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
         const RenderAreaBuilderVariant& renderAreaBuilder,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, bool isProtected, bool attachGainmap,
         const sp<IScreenCaptureListener>& captureListener,
-        std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
+        std::optional<OutputCompositionState>& displayState,
+        std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
     SFTRACE_CALL();
 
     ScreenCaptureResults captureResults;
@@ -7375,11 +7518,9 @@
     float displayBrightnessNits = displayState.value().displayBrightnessNits;
     float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
 
-    // Empty vector needed to pass into renderScreenImpl for legacy path
-    std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
     ftl::SharedFuture<FenceResult> renderFuture =
             renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
-                             attachGainmap, captureResults, displayState, layers, layerFEs);
+                             captureResults, displayState, layers);
 
     if (captureResults.capturedHdrLayers && attachGainmap &&
         FlagManager::getInstance().true_hdr_screenshots()) {
@@ -7414,8 +7555,7 @@
             ScreenCaptureResults unusedResults;
             ftl::SharedFuture<FenceResult> hdrRenderFuture =
                     renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
-                                     isProtected, attachGainmap, unusedResults, displayState,
-                                     layers, layerFEs);
+                                     isProtected, unusedResults, displayState, layers);
 
             renderFuture =
                     ftl::Future(std::move(renderFuture))
@@ -7461,77 +7601,14 @@
     return renderFuture;
 }
 
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
-        RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
-        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, bool attachGainmap,
-        const sp<IScreenCaptureListener>& captureListener) {
-    SFTRACE_CALL();
-
-    auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
-                                    kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
-        auto layers = getLayerSnapshotsFn();
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            for (auto& [layer, layerFE] : layers) {
-                attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
-            }
-        }
-        auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
-
-        ScreenCaptureResults captureResults;
-        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) {
-                captureResults.fenceResult = base::unexpected(NO_MEMORY);
-                captureListener->onScreenCaptureCompleted(captureResults);
-            }
-            return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
-        }
-
-        auto layerFEs = extractLayerFEs(layers);
-        ftl::SharedFuture<FenceResult> renderFuture =
-                renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
-                                 attachGainmap, captureResults, displayState, layers, layerFEs);
-
-        if (captureListener) {
-            // Defer blocking on renderFuture back to the Binder thread.
-            return ftl::Future(std::move(renderFuture))
-                    .then([captureListener, captureResults = std::move(captureResults)](
-                                  FenceResult fenceResult) mutable -> FenceResult {
-                        captureResults.fenceResult = std::move(fenceResult);
-                        captureListener->onScreenCaptureCompleted(captureResults);
-                        return base::unexpected(NO_ERROR);
-                    })
-                    .share();
-        }
-        return renderFuture;
-    };
-
-    // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number
-    // of calls on the main thread.
-    auto future =
-            mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
-
-    // Flatten nested futures.
-    auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
-        return future;
-    });
-
-    return chain.share();
-}
-
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
         const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
-        bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
-        ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState,
-        std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
+        bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
+        std::optional<OutputCompositionState>& displayState,
+        std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) {
     SFTRACE_CALL();
 
-    for (auto& layerFE : layerFEs) {
+    for (auto& [_, layerFE] : layers) {
         frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
         captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
         captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
@@ -7590,29 +7667,32 @@
     captureResults.buffer = capturedBuffer->getBuffer();
 
     ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
-    if (!layerFEs.empty()) {
-        const sp<LayerFE>& layerFE = layerFEs.back();
+    if (!layers.empty()) {
+        const sp<LayerFE>& layerFE = layers.back().second;
         layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
     }
 
-    auto copyLayerFEs = [&layerFEs]() {
-        std::vector<sp<compositionengine::LayerFE>> ceLayerFEs;
-        ceLayerFEs.reserve(layerFEs.size());
-        for (const auto& layerFE : layerFEs) {
-            ceLayerFEs.push_back(layerFE);
-        }
-        return ceLayerFEs;
-    };
-
     auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
                     sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
-                    layerFEs = copyLayerFEs(), layerStack, regionSampling,
+                    layers = std::move(layers), layerStack, regionSampling,
                     renderArea = std::move(renderArea), renderIntent,
                     enableLocalTonemapping]() -> FenceResult {
         std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
                 mFactory.createCompositionEngine();
         compositionEngine->setRenderEngine(mRenderEngine.get());
 
+        std::vector<sp<compositionengine::LayerFE>> layerFEs;
+        layerFEs.reserve(layers.size());
+        for (auto& [layer, layerFE] : layers) {
+            // Release fences were not yet added for non-threaded render engine. To avoid
+            // deadlocks between main thread and binder threads waiting for the future fence
+            // result, fences should be added to layers in the same hop onto the main thread.
+            if (!mRenderEngine->isThreaded()) {
+                attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+            }
+            layerFEs.push_back(layerFE);
+        }
+
         compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
                                                              .renderIntent = renderIntent};
 
@@ -7670,36 +7750,9 @@
     //
     // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
     // to CompositionEngine::present.
-    ftl::SharedFuture<FenceResult> presentFuture;
-    if (FlagManager::getInstance().single_hop_screenshot() &&
-        FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
-        presentFuture = ftl::yield(present()).share();
-    } else {
-        presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
-                                                    : ftl::yield(present()).share();
-    }
-
-    if (!FlagManager::getInstance().ce_fence_promise()) {
-        for (auto& [layer, layerFE] : layers) {
-            layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
-                                    [layerFE = std::move(layerFE)](FenceResult) {
-                                        if (FlagManager::getInstance()
-                                                    .screenshot_fence_preservation()) {
-                                            const auto compositionResult =
-                                                    layerFE->stealCompositionResult();
-                                            const auto& fences = compositionResult.releaseFences;
-                                            // CompositionEngine may choose to cull layers that
-                                            // aren't visible, so pass a non-fence.
-                                            return fences.empty() ? Fence::NO_FENCE
-                                                                  : fences.back().first.get();
-                                        } else {
-                                            return layerFE->stealCompositionResult()
-                                                    .releaseFences.back()
-                                                    .first.get();
-                                        }
-                                    });
-        }
-    }
+    ftl::SharedFuture<FenceResult> presentFuture = mRenderEngine->isThreaded()
+            ? ftl::yield(present()).share()
+            : mScheduler->schedule(std::move(present)).share();
 
     return presentFuture;
 }
@@ -7763,7 +7816,8 @@
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     if (const bool isPacesetter =
-                mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
+                mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(),
+                                                 /*clearContentRequirements*/ true)) {
         mDisplayModeController.updateKernelIdleTimer(displayId);
     }
 
@@ -8181,6 +8235,14 @@
     }));
 }
 
+void SurfaceFlinger::setActivePictureListener(const sp<gui::IActivePictureListener>& listener) {
+    if (com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        Mutex::Autolock lock(mStateLock);
+        mActivePictureListener = listener;
+        mHaveNewActivePictureListener = listener != nullptr;
+    }
+}
+
 std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
         BufferData& bufferData, const char* layerName, uint64_t transactionId) {
     if (bufferData.buffer &&
@@ -8679,6 +8741,15 @@
 
     outInfo->activeDisplayModeId = info.activeDisplayModeId;
     outInfo->renderFrameRate = info.renderFrameRate;
+    outInfo->hasArrSupport = info.hasArrSupport;
+    gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate;
+    frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal();
+    frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh();
+    outInfo->supportedRefreshRates.clear();
+    outInfo->supportedRefreshRates.reserve(info.supportedRefreshRates.size());
+    for (float supportedRefreshRate : info.supportedRefreshRates) {
+        outInfo->supportedRefreshRates.push_back(supportedRefreshRate);
+    }
 
     outInfo->supportedColorModes.clear();
     outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
@@ -8834,6 +8905,16 @@
     return binder::Status::ok();
 }
 
+binder::Status SurfaceComposerAIDL::getMaxLayerPictureProfiles(const sp<IBinder>& display,
+                                                               int32_t* outMaxProfiles) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+    mFlinger->getMaxLayerPictureProfiles(display, outMaxProfiles);
+    return binder::Status::ok();
+}
+
 binder::Status SurfaceComposerAIDL::captureDisplay(
         const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
     mFlinger->captureDisplay(args, captureListener);
@@ -9112,6 +9193,15 @@
     return binderStatusFromStatusT(status);
 }
 
+binder::Status SurfaceComposerAIDL::setActivePictureListener(
+        const sp<gui::IActivePictureListener>& listener) {
+    status_t status = checkObservePictureProfilesPermission();
+    if (status == OK) {
+        mFlinger->setActivePictureListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
 binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) {
     status_t status = checkAccessPermission();
     if (status == OK) {
@@ -9182,26 +9272,46 @@
 }
 
 binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_enableRefreshRateOverlay(active);
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_setDebugFlash(delay);
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::scheduleComposite() {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_scheduleComposite();
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::scheduleCommit() {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_scheduleCommit();
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::forceClientComposition(bool enabled) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->sfdo_forceClientComposition(enabled);
     return binder::Status::ok();
 }
@@ -9367,6 +9477,17 @@
     return OK;
 }
 
+status_t SurfaceComposerAIDL::checkObservePictureProfilesPermission() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(sObservePictureProfiles, pid, uid)) {
+        ALOGE("Permission Denial: can't manage picture profiles pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+    return OK;
+}
+
 void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
     static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1ccfdc9..44856ae 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -24,9 +24,11 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/thread_annotations.h>
+#include <android/gui/ActivePicture.h>
 #include <android/gui/BnSurfaceComposer.h>
 #include <android/gui/DisplayStatInfo.h>
 #include <android/gui/DisplayState.h>
+#include <android/gui/IActivePictureListener.h>
 #include <android/gui/IJankListener.h>
 #include <android/gui/ISurfaceComposerClient.h>
 #include <common/trace.h>
@@ -57,6 +59,7 @@
 #include <utils/threads.h>
 
 #include <compositionengine/OutputColorSetting.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <scheduler/Fps.h>
 #include <scheduler/PresentLatencyTracker.h>
 #include <scheduler/Time.h>
@@ -66,11 +69,13 @@
 #include <ui/FenceResult.h>
 
 #include <common/FlagManager.h>
+#include "ActivePictureUpdater.h"
+#include "BackgroundExecutor.h"
 #include "Display/DisplayModeController.h"
 #include "Display/PhysicalDisplay.h"
+#include "Display/VirtualDisplaySnapshot.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
-#include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FrontEnd/DisplayInfo.h"
@@ -81,6 +86,7 @@
 #include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
 #include "MutexUtils.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 #include "Scheduler/ISchedulerCallback.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/Scheduler.h"
@@ -92,6 +98,7 @@
 #include "TransactionState.h"
 #include "Utils/OnceFuture.h"
 
+#include <algorithm>
 #include <atomic>
 #include <cstdint>
 #include <functional>
@@ -296,7 +303,7 @@
     // Called when all clients have released all their references to
     // this layer. The layer may still be kept alive by its parents but
     // the client can no longer modify this layer directly.
-    void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
+    void onHandleDestroyed(sp<Layer>& layer, uint32_t layerId);
 
     TransactionCallbackInvoker& getTransactionCallbackInvoker() {
         return mTransactionCallbackInvoker;
@@ -433,32 +440,32 @@
     // This is done to avoid lock contention with the main thread.
     class BufferCountTracker {
     public:
-        void increment(BBinder* layerHandle) {
+        void increment(uint32_t layerId) {
             std::lock_guard<std::mutex> lock(mLock);
-            auto it = mCounterByLayerHandle.find(layerHandle);
-            if (it != mCounterByLayerHandle.end()) {
+            auto it = mCounterByLayerId.find(layerId);
+            if (it != mCounterByLayerId.end()) {
                 auto [name, pendingBuffers] = it->second;
                 int32_t count = ++(*pendingBuffers);
                 SFTRACE_INT(name.c_str(), count);
             } else {
-                ALOGW("Handle not found! %p", layerHandle);
+                ALOGW("Layer ID not found! %d", layerId);
             }
         }
 
-        void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
+        void add(uint32_t layerId, const std::string& name, std::atomic<int32_t>* counter) {
             std::lock_guard<std::mutex> lock(mLock);
-            mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
+            mCounterByLayerId[layerId] = std::make_pair(name, counter);
         }
 
-        void remove(BBinder* layerHandle) {
+        void remove(uint32_t layerId) {
             std::lock_guard<std::mutex> lock(mLock);
-            mCounterByLayerHandle.erase(layerHandle);
+            mCounterByLayerId.erase(layerId);
         }
 
     private:
         std::mutex mLock;
-        std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
-                mCounterByLayerHandle GUARDED_BY(mLock);
+        std::unordered_map<uint32_t, std::pair<std::string, std::atomic<int32_t>*>>
+                mCounterByLayerId GUARDED_BY(mLock);
     };
 
     enum class BootStage {
@@ -590,6 +597,7 @@
     status_t getHdrOutputConversionSupport(bool* outSupport) const;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
+    status_t getMaxLayerPictureProfiles(const sp<IBinder>& displayToken, int32_t* outMaxProfiles);
     void setPowerMode(const sp<IBinder>& displayToken, int mode);
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
                               const std::vector<ui::Hdr>& hdrTypes);
@@ -659,6 +667,8 @@
 
     void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
 
+    void setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+
     // IBinder::DeathRecipient overrides:
     void binderDied(const wp<IBinder>& who) override;
 
@@ -691,7 +701,7 @@
     void onChoreographerAttached() override;
     void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
                                      Fps renderRate) override;
-    void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
+    void onCommitNotComposited() override
             REQUIRES(kMainThreadContext);
     void vrrDisplayIdle(bool idle) override;
 
@@ -851,13 +861,14 @@
     void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
 
     // Checks if a protected layer exists in a list of layers.
-    bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const;
+    bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
 
     using OutputCompositionState = compositionengine::impl::OutputCompositionState;
 
     std::optional<OutputCompositionState> getSnapshotsFromMainThread(
             RenderAreaBuilderVariant& renderAreaBuilder,
-            GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs);
+            GetLayerSnapshotsFunction getLayerSnapshotsFn,
+            std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
 
     void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
                              ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
@@ -866,32 +877,19 @@
     std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
             RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
 
-    // Legacy layer raw pointer is not safe to access outside the main thread.
-    // Creates a new vector consisting only of LayerFEs, which can be safely
-    // accessed outside the main thread.
-    std::vector<sp<LayerFE>> extractLayerFEs(
-            const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
-
     ftl::SharedFuture<FenceResult> captureScreenshot(
             const RenderAreaBuilderVariant& renderAreaBuilder,
             const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
             bool grayscale, bool isProtected, bool attachGainmap,
             const sp<IScreenCaptureListener>& captureListener,
             std::optional<OutputCompositionState>& displayState,
-            std::vector<sp<LayerFE>>& layerFEs);
-
-    ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
-            RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
-            const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
-            bool grayscale, bool isProtected, bool attachGainmap,
-            const sp<IScreenCaptureListener>&);
+            std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
 
     ftl::SharedFuture<FenceResult> renderScreenImpl(
             const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
-            bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
-            ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState,
-            std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
-            std::vector<sp<LayerFE>>& layerFEs);
+            bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&,
+            std::optional<OutputCompositionState>& displayState,
+            std::vector<std::pair<Layer*, sp<LayerFE>>>& layers);
 
     bool canAllocateHwcDisplayIdForVDS(uint64_t usage);
 
@@ -1080,9 +1078,21 @@
     void enableHalVirtualDisplays(bool);
 
     // Virtual display lifecycle for ID generation and HAL allocation.
-    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, bool canAllocateHwcForVDS)
+    VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat, const std::string& uniqueId,
+            bool canAllocateHwcForVDS)
             REQUIRES(mStateLock);
+    template <typename ID>
+    void acquireVirtualDisplaySnapshot(ID displayId, const std::string& uniqueId) {
+        std::lock_guard lock(mVirtualDisplaysMutex);
+        const bool emplace_success =
+                mVirtualDisplays.try_emplace(displayId, displayId, uniqueId).second;
+        if (!emplace_success) {
+            ALOGW("%s: Virtual display snapshot with the same ID already exists", __func__);
+        }
+    }
+
     void releaseVirtualDisplay(VirtualDisplayId);
+    void releaseVirtualDisplaySnapshot(VirtualDisplayId displayId);
 
     // Returns a display other than `mActiveDisplayId` that can be activated, if any.
     sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
@@ -1224,6 +1234,14 @@
 
     bool mHdrLayerInfoChanged = false;
 
+    struct LayerEvent {
+        uid_t uid;
+        int32_t layerId;
+        ui::Dataspace dataspace;
+        std::chrono::milliseconds timeSinceLastEvent;
+    };
+    std::vector<LayerEvent> mLayerEvents;
+
     // Used to ensure we omit a callback when HDR layer info listener is newly added but the
     // scene hasn't changed
     bool mAddingHDRLayerInfoListener = false;
@@ -1248,7 +1266,6 @@
     // latched.
     std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames;
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
-    std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
 
     // Sorted list of layers that were composed during previous frame. This is used to
     // avoid an expensive traversal of the layer hierarchy when there are no
@@ -1276,6 +1293,10 @@
 
     display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
 
+    mutable std::mutex mVirtualDisplaysMutex;
+    ftl::SmallMap<VirtualDisplayId, const display::VirtualDisplaySnapshot, 2> mVirtualDisplays
+            GUARDED_BY(mVirtualDisplaysMutex);
+
     // The inner or outer display for foldables, while unfolded or folded, respectively.
     std::atomic<PhysicalDisplayId> mActiveDisplayId;
 
@@ -1368,7 +1389,7 @@
     sp<os::IInputFlinger> mInputFlinger;
     InputWindowCommands mInputWindowCommands;
 
-    std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
+    std::unique_ptr<adpf::PowerAdvisor> mPowerAdvisor;
 
     void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
@@ -1377,11 +1398,17 @@
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
 
+    // Tracks the number of maximum queued buffers by layer owner Uid.
+    using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>;
     BufferCountTracker mBufferCountTracker;
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
 
+    sp<gui::IActivePictureListener> mActivePictureListener GUARDED_BY(mStateLock);
+    bool mHaveNewActivePictureListener GUARDED_BY(mStateLock);
+    ActivePictureUpdater mActivePictureUpdater GUARDED_BY(kMainThreadContext);
+
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
     // Must only be accessed on the main thread.
@@ -1422,6 +1449,11 @@
     // Whether a display should be turned on when initialized
     bool mSkipPowerOnForQuiescent;
 
+    // used for omitting vsync callbacks to apps when the display is not updatable
+    int mRefreshableDisplays GUARDED_BY(mStateLock) = 0;
+    void incRefreshableDisplays() REQUIRES(mStateLock);
+    void decRefreshableDisplays() REQUIRES(mStateLock);
+
     frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext);
     frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
     frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
@@ -1526,6 +1558,8 @@
     binder::Status getHdrOutputConversionSupport(bool* outSupport) override;
     binder::Status setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override;
     binder::Status setGameContentType(const sp<IBinder>& display, bool on) override;
+    binder::Status getMaxLayerPictureProfiles(const sp<IBinder>& display,
+                                              int32_t* outMaxProfiles) override;
     binder::Status captureDisplay(const DisplayCaptureArgs&,
                                   const sp<IScreenCaptureListener>&) override;
     binder::Status captureDisplayById(int64_t, const CaptureArgs&,
@@ -1614,12 +1648,15 @@
     binder::Status flushJankData(int32_t layerId) override;
     binder::Status removeJankListener(int32_t layerId, const sp<gui::IJankListener>& listener,
                                       int64_t afterVsync) override;
+    binder::Status setActivePictureListener(const sp<gui::IActivePictureListener>& listener);
+    binder::Status clearActivePictureListener();
 
 private:
     static const constexpr bool kUsePermissionCache = true;
     status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
     status_t checkControlDisplayBrightnessPermission();
     status_t checkReadFrameBufferPermission();
+    status_t checkObservePictureProfilesPermission();
     static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
                                               gui::DynamicDisplayInfo*& outInfo);
 
diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp
index ed1e2ec..cc0063c 100644
--- a/services/surfaceflinger/Tracing/LayerDataSource.cpp
+++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp
@@ -82,10 +82,13 @@
     }
 }
 
-void LayerDataSource::OnStop(const LayerDataSource::StopArgs&) {
+void LayerDataSource::OnStop(const LayerDataSource::StopArgs& args) {
     ALOGD("Received OnStop event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
     if (auto* p = mLayerTracing.load()) {
-        p->onStop(mMode);
+        // In dump mode we need to defer the stop (through HandleStopAsynchronously()) till
+        // the layers snapshot has been captured and written to perfetto. We must avoid writing
+        // to perfetto within the OnStop callback to prevent deadlocks (b/313130597).
+        p->onStop(mMode, mFlags, args.HandleStopAsynchronously());
     }
 }
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index d40b888..d78f9bb 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -32,7 +32,7 @@
 namespace android {
 
 LayerTracing::LayerTracing() {
-    mTakeLayersSnapshotProto = [](uint32_t) { return perfetto::protos::LayersSnapshotProto{}; };
+    mTakeLayersSnapshotProto = [](uint32_t, const OnLayersSnapshotCallback&) {};
     LayerDataSource::Initialize(*this);
 }
 
@@ -45,7 +45,7 @@
 }
 
 void LayerTracing::setTakeLayersSnapshotProtoFunction(
-        const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>& callback) {
+        const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>& callback) {
     mTakeLayersSnapshotProto = callback;
 }
 
@@ -62,7 +62,10 @@
             // It might take a while before a layers change occurs and a "spontaneous" snapshot is
             // taken. Let's manually take a snapshot, so that the trace's first entry will contain
             // the current layers state.
-            addProtoSnapshotToOstream(mTakeLayersSnapshotProto(flags), Mode::MODE_ACTIVE);
+            auto onLayersSnapshot = [this](perfetto::protos::LayersSnapshotProto&& snapshot) {
+                addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_ACTIVE);
+            };
+            mTakeLayersSnapshotProto(flags, onLayersSnapshot);
             ALOGD("Started active tracing (traced initial snapshot)");
             break;
         }
@@ -89,9 +92,7 @@
             break;
         }
         case Mode::MODE_DUMP: {
-            auto snapshot = mTakeLayersSnapshotProto(flags);
-            addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
-            ALOGD("Started dump tracing (dumped single snapshot)");
+            ALOGD("Started dump tracing");
             break;
         }
         default: {
@@ -125,10 +126,27 @@
     ALOGD("Flushed generated tracing");
 }
 
-void LayerTracing::onStop(Mode mode) {
-    if (mode == Mode::MODE_ACTIVE) {
-        mIsActiveTracingStarted.store(false);
-        ALOGD("Stopped active tracing");
+void LayerTracing::onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone) {
+    switch (mode) {
+        case Mode::MODE_ACTIVE: {
+            mIsActiveTracingStarted.store(false);
+            deferredStopDone();
+            ALOGD("Stopped active tracing");
+            break;
+        }
+        case Mode::MODE_DUMP: {
+            auto onLayersSnapshot = [this, deferredStopDone = std::move(deferredStopDone)](
+                                            perfetto::protos::LayersSnapshotProto&& snapshot) {
+                addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
+                deferredStopDone();
+                ALOGD("Stopped dump tracing (written single snapshot)");
+            };
+            mTakeLayersSnapshotProto(flags, onLayersSnapshot);
+            break;
+        }
+        default: {
+            deferredStopDone();
+        }
     }
 }
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 2895ba7..e99fe4c 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -87,6 +87,7 @@
 class LayerTracing {
 public:
     using Mode = perfetto::protos::pbzero::SurfaceFlingerLayersConfig::Mode;
+    using OnLayersSnapshotCallback = std::function<void(perfetto::protos::LayersSnapshotProto&&)>;
 
     enum Flag : uint32_t {
         TRACE_INPUT = 1 << 1,
@@ -102,7 +103,7 @@
     LayerTracing(std::ostream&);
     ~LayerTracing();
     void setTakeLayersSnapshotProtoFunction(
-            const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>&);
+            const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>&);
     void setTransactionTracing(TransactionTracing&);
 
     // Start event from perfetto data source
@@ -110,7 +111,7 @@
     // Flush event from perfetto data source
     void onFlush(Mode mode, uint32_t flags, bool isBugreport);
     // Stop event from perfetto data source
-    void onStop(Mode mode);
+    void onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone);
 
     void addProtoSnapshotToOstream(perfetto::protos::LayersSnapshotProto&& snapshot, Mode mode);
     bool isActiveTracingStarted() const;
@@ -123,7 +124,7 @@
     void writeSnapshotToPerfetto(const perfetto::protos::LayersSnapshotProto& snapshot, Mode mode);
     bool checkAndUpdateLastVsyncIdWrittenToPerfetto(Mode mode, std::int64_t vsyncId);
 
-    std::function<perfetto::protos::LayersSnapshotProto(uint32_t)> mTakeLayersSnapshotProto;
+    std::function<void(uint32_t, const OnLayersSnapshotCallback&)> mTakeLayersSnapshotProto;
     TransactionTracing* mTransactionTracing;
 
     std::atomic<bool> mIsActiveTracingStarted{false};
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index b189598..f39a4d2 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -147,7 +147,7 @@
         proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
     }
     if (layer.what & layer_state_t::eCropChanged) {
-        LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+        LayerProtoHelper::writeToProto(Rect(layer.crop), proto.mutable_crop());
     }
     if (layer.what & layer_state_t::eBufferChanged) {
         perfetto::protos::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index c6856ae..b22ec66 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -28,7 +28,6 @@
 #include "Utils/FenceUtils.h"
 
 #include <binder/IInterface.h>
-#include <common/FlagManager.h>
 #include <common/trace.h>
 #include <utils/RefBase.h>
 
@@ -127,14 +126,8 @@
     if (surfaceControl) {
         sp<Fence> prevFence = nullptr;
 
-        if (FlagManager::getInstance().ce_fence_promise()) {
-            for (auto& future : handle->previousReleaseFences) {
-                mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
-            }
-        } else {
-            for (const auto& future : handle->previousSharedReleaseFences) {
-                mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
-            }
+        for (auto& future : handle->previousReleaseFences) {
+            mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
         }
 
         handle->previousReleaseFence = prevFence;
@@ -151,7 +144,7 @@
                                                     eventStats, handle->previousReleaseCallbackId);
         if (handle->bufferReleaseChannel &&
             handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
-            mBufferReleases.emplace_back(handle->bufferReleaseChannel,
+            mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel,
                                          handle->previousReleaseCallbackId,
                                          handle->previousReleaseFence,
                                          handle->currentMaxAcquiredBufferCount);
@@ -166,8 +159,13 @@
 
 void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
     for (const auto& bufferRelease : mBufferReleases) {
-        bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
-                                                 bufferRelease.currentMaxAcquiredBufferCount);
+        status_t status = bufferRelease.channel
+                                  ->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence,
+                                                      bufferRelease.currentMaxAcquiredBufferCount);
+        if (status != OK) {
+            ALOGE("[%s] writeReleaseFence failed. error %d (%s)", bufferRelease.layerName.c_str(),
+                  -status, strerror(-status));
+        }
     }
     mBufferReleases.clear();
 
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 14a7487..178ddbb 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -43,7 +43,6 @@
     std::string name;
     sp<Fence> previousReleaseFence;
     std::vector<ftl::Future<FenceResult>> previousReleaseFences;
-    std::vector<ftl::SharedFuture<FenceResult>> previousSharedReleaseFences;
     std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     nsecs_t latchTime = -1;
     std::optional<uint32_t> transformHint = std::nullopt;
@@ -84,6 +83,7 @@
         mCompletedTransactions;
 
     struct BufferRelease {
+        std::string layerName;
         std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel;
         ReleaseCallbackId callbackId;
         sp<Fence> fence;
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f9c99bf..8786f6e 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -38,6 +38,7 @@
     ],
     static_libs: [
         "libsurfaceflingerflags",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -51,6 +52,7 @@
     ],
     static_libs: [
         "libsurfaceflingerflags_test",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -67,6 +69,7 @@
     static_libs: [
         "libsurfaceflinger_common",
         "libsurfaceflingerflags",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
@@ -83,6 +86,7 @@
     static_libs: [
         "libsurfaceflinger_common_test",
         "libsurfaceflingerflags_test",
+        "aconfig_hardware_flags_c_lib",
         "android.os.flags-aconfig-cc-test",
         "android.server.display.flags-aconfig-cc",
         "libguiflags_no_apex",
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 12d6138..5e78426 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -27,6 +27,7 @@
 #include <cinttypes>
 
 #include <android_os.h>
+#include <android_hardware_flags.h>
 #include <com_android_graphics_libgui_flags.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
 #include <com_android_server_display_feature_flags.h>
@@ -89,9 +90,9 @@
     mBootCompleted = true;
 }
 
-void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name,
+void FlagManager::dumpFlag(std::string& result, bool aconfig, const char* name,
                            std::function<bool()> getter) const {
-    if (readonly || mBootCompleted) {
+    if (aconfig || mBootCompleted) {
         base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false");
     } else {
         base::StringAppendF(&result, "%s: in progress (still booting)\n", name);
@@ -99,67 +100,75 @@
 }
 
 void FlagManager::dump(std::string& result) const {
-#define DUMP_FLAG_INTERVAL(name, readonly) \
-    dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this))
-#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false)
-#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true)
+#define DUMP_FLAG_INTERNAL(name, aconfig) \
+    dumpFlag(result, (aconfig), #name, std::bind(&FlagManager::name, this))
+#define DUMP_LEGACY_SERVER_FLAG(name) DUMP_FLAG_INTERNAL(name, false)
+#define DUMP_ACONFIG_FLAG(name) DUMP_FLAG_INTERNAL(name, true)
 
     base::StringAppendF(&result, "FlagManager values: \n");
 
     /// Legacy server flags ///
-    DUMP_SERVER_FLAG(use_adpf_cpu_hint);
-    DUMP_SERVER_FLAG(use_skia_tracing);
+    DUMP_LEGACY_SERVER_FLAG(use_adpf_cpu_hint);
+    DUMP_LEGACY_SERVER_FLAG(use_skia_tracing);
 
-    /// Trunk stable server flags ///
-    DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
-    DUMP_SERVER_FLAG(adpf_gpu_sf);
-    DUMP_SERVER_FLAG(adpf_use_fmq_channel);
+    /// Trunk stable server (R/W) flags ///
+    DUMP_ACONFIG_FLAG(refresh_rate_overlay_on_external_display);
+    DUMP_ACONFIG_FLAG(adpf_gpu_sf);
+    DUMP_ACONFIG_FLAG(adpf_native_session_manager);
+    DUMP_ACONFIG_FLAG(adpf_use_fmq_channel);
+    DUMP_ACONFIG_FLAG(graphite_renderengine_preview_rollout);
 
     /// Trunk stable readonly flags ///
-    DUMP_READ_ONLY_FLAG(connected_display);
-    DUMP_READ_ONLY_FLAG(enable_small_area_detection);
-    DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
-    DUMP_READ_ONLY_FLAG(misc1);
-    DUMP_READ_ONLY_FLAG(vrr_config);
-    DUMP_READ_ONLY_FLAG(hotplug2);
-    DUMP_READ_ONLY_FLAG(hdcp_level_hal);
-    DUMP_READ_ONLY_FLAG(multithreaded_present);
-    DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace);
-    DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency);
-    DUMP_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved);
-    DUMP_READ_ONLY_FLAG(enable_fro_dependent_features);
-    DUMP_READ_ONLY_FLAG(display_protected);
-    DUMP_READ_ONLY_FLAG(fp16_client_target);
-    DUMP_READ_ONLY_FLAG(game_default_frame_rate);
-    DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
-    DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
-    DUMP_READ_ONLY_FLAG(vulkan_renderengine);
-    DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
-    DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
-    DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame);
-    DUMP_READ_ONLY_FLAG(restore_blur_step);
-    DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
-    DUMP_READ_ONLY_FLAG(protected_if_client);
-    DUMP_READ_ONLY_FLAG(ce_fence_promise);
-    DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
-    DUMP_READ_ONLY_FLAG(graphite_renderengine);
-    DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
-    DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
-    DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
-    DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter);
-    DUMP_READ_ONLY_FLAG(detached_mirror);
-    DUMP_READ_ONLY_FLAG(commit_not_composited);
-    DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size);
-    DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
-    DUMP_READ_ONLY_FLAG(override_trusted_overlay);
-    DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
-    DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine);
-    DUMP_READ_ONLY_FLAG(single_hop_screenshot);
-    DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
-    DUMP_READ_ONLY_FLAG(true_hdr_screenshots);
+    DUMP_ACONFIG_FLAG(adpf_fmq_sf);
+    DUMP_ACONFIG_FLAG(arr_setframerate_gte_enum);
+    DUMP_ACONFIG_FLAG(connected_display);
+    DUMP_ACONFIG_FLAG(enable_small_area_detection);
+    DUMP_ACONFIG_FLAG(stable_edid_ids);
+    DUMP_ACONFIG_FLAG(frame_rate_category_mrr);
+    DUMP_ACONFIG_FLAG(misc1);
+    DUMP_ACONFIG_FLAG(vrr_config);
+    DUMP_ACONFIG_FLAG(hdcp_level_hal);
+    DUMP_ACONFIG_FLAG(multithreaded_present);
+    DUMP_ACONFIG_FLAG(add_sf_skipped_frames_to_trace);
+    DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
+    DUMP_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved);
+    DUMP_ACONFIG_FLAG(enable_fro_dependent_features);
+    DUMP_ACONFIG_FLAG(display_protected);
+    DUMP_ACONFIG_FLAG(fp16_client_target);
+    DUMP_ACONFIG_FLAG(game_default_frame_rate);
+    DUMP_ACONFIG_FLAG(enable_layer_command_batching);
+    DUMP_ACONFIG_FLAG(vulkan_renderengine);
+    DUMP_ACONFIG_FLAG(renderable_buffer_usage);
+    DUMP_ACONFIG_FLAG(vrr_bugfix_24q4);
+    DUMP_ACONFIG_FLAG(vrr_bugfix_dropped_frame);
+    DUMP_ACONFIG_FLAG(restore_blur_step);
+    DUMP_ACONFIG_FLAG(dont_skip_on_early_ro);
+    DUMP_ACONFIG_FLAG(no_vsyncs_on_screen_off);
+    DUMP_ACONFIG_FLAG(protected_if_client);
+    DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout);
+    DUMP_ACONFIG_FLAG(graphite_renderengine);
+    DUMP_ACONFIG_FLAG(filter_frames_before_trace_starts);
+    DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed);
+    DUMP_ACONFIG_FLAG(deprecate_vsync_sf);
+    DUMP_ACONFIG_FLAG(allow_n_vsyncs_in_targeter);
+    DUMP_ACONFIG_FLAG(detached_mirror);
+    DUMP_ACONFIG_FLAG(commit_not_composited);
+    DUMP_ACONFIG_FLAG(correct_dpi_with_display_size);
+    DUMP_ACONFIG_FLAG(local_tonemap_screenshots);
+    DUMP_ACONFIG_FLAG(override_trusted_overlay);
+    DUMP_ACONFIG_FLAG(flush_buffer_slots_to_uncache);
+    DUMP_ACONFIG_FLAG(force_compile_graphite_renderengine);
+    DUMP_ACONFIG_FLAG(trace_frame_rate_override);
+    DUMP_ACONFIG_FLAG(true_hdr_screenshots);
+    DUMP_ACONFIG_FLAG(display_config_error_hal);
+    DUMP_ACONFIG_FLAG(connected_display_hdr);
+    DUMP_ACONFIG_FLAG(deprecate_frame_tracker);
+    DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
+    DUMP_ACONFIG_FLAG(begone_bright_hlg);
+    DUMP_ACONFIG_FLAG(window_blur_kawase2);
 
-#undef DUMP_READ_ONLY_FLAG
-#undef DUMP_SERVER_FLAG
+#undef DUMP_ACONFIG_FLAG
+#undef DUMP_LEGACY_SERVER_FLAG
 #undef DUMP_FLAG_INTERVAL
 }
 
@@ -184,35 +193,24 @@
         return getServerConfigurableFlag(serverFlagName);                                   \
     }
 
-#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted, owner)         \
-    bool FlagManager::name() const {                                                            \
-        if (checkForBootCompleted) {                                                            \
-            LOG_ALWAYS_FATAL_IF(!mBootCompleted,                                                \
-                                "Can't read %s before boot completed as it is server writable", \
-                                __func__);                                                      \
-        }                                                                                       \
-        static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride);      \
-        static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride);   \
-        if (mUnitTestMode) {                                                                    \
-            /*                                                                                  \
-             * When testing, we don't want to rely on the cached `value` or the debugOverride.  \
-             */                                                                                 \
-            return owner ::name();                                                              \
-        }                                                                                       \
-        return value;                                                                           \
+#define FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner)                            \
+    bool FlagManager::name() const {                                                           \
+        static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride);     \
+        static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride);  \
+        if (mUnitTestMode) {                                                                   \
+            /*                                                                                 \
+             * When testing, we don't want to rely on the cached `value` or the debugOverride. \
+             */                                                                                \
+            return owner ::name();                                                             \
+        }                                                                                      \
+        return value;                                                                          \
     }
 
-#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
-    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, flags)
+#define FLAG_MANAGER_ACONFIG_FLAG(name, syspropOverride) \
+    FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, flags)
 
-#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
-    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, flags)
-
-#define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \
-    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner)
-
-#define FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(name, syspropOverride, owner) \
-    FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, owner)
+#define FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(name, syspropOverride, owner) \
+    FLAG_MANAGER_ACONFIG_INTERNAL(name, syspropOverride, owner)
 
 /// Legacy server flags ///
 FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
@@ -222,59 +220,68 @@
                                 "SkiaTracingFeature__use_skia_tracing")
 
 /// Trunk stable readonly flags ///
-FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
-FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
-FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
-FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
-FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "")
-FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
-FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "")
-FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "")
-FLAG_MANAGER_READ_ONLY_FLAG(cache_when_source_crop_layer_only_moved,
-                            "debug.sf.cache_source_crop_only_moved")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "")
-FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "")
-FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
-FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
-FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
-FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
-FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
-FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
-FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
-FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
-FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
-FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
-FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
-FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
-FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
-FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
-FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, "");
-FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, "");
-FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
-FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, "");
-FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
-FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
-FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
-FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
-FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
-FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(adpf_fmq_sf, "")
+FLAG_MANAGER_ACONFIG_FLAG(arr_setframerate_gte_enum, "debug.sf.arr_setframerate_gte_enum")
+FLAG_MANAGER_ACONFIG_FLAG(connected_display, "")
+FLAG_MANAGER_ACONFIG_FLAG(enable_small_area_detection, "")
+FLAG_MANAGER_ACONFIG_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids")
+FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
+FLAG_MANAGER_ACONFIG_FLAG(misc1, "")
+FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config")
+FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "")
+FLAG_MANAGER_ACONFIG_FLAG(multithreaded_present, "debug.sf.multithreaded_present")
+FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "")
+FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "")
+FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved,
+                          "debug.sf.cache_source_crop_only_moved")
+FLAG_MANAGER_ACONFIG_FLAG(enable_fro_dependent_features, "")
+FLAG_MANAGER_ACONFIG_FLAG(display_protected, "")
+FLAG_MANAGER_ACONFIG_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
+FLAG_MANAGER_ACONFIG_FLAG(game_default_frame_rate, "")
+FLAG_MANAGER_ACONFIG_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
+FLAG_MANAGER_ACONFIG_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
+FLAG_MANAGER_ACONFIG_FLAG(renderable_buffer_usage, "")
+FLAG_MANAGER_ACONFIG_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
+FLAG_MANAGER_ACONFIG_FLAG(dont_skip_on_early_ro, "")
+FLAG_MANAGER_ACONFIG_FLAG(no_vsyncs_on_screen_off, "debug.sf.no_vsyncs_on_screen_off")
+FLAG_MANAGER_ACONFIG_FLAG(protected_if_client, "")
+FLAG_MANAGER_ACONFIG_FLAG(vrr_bugfix_24q4, "");
+FLAG_MANAGER_ACONFIG_FLAG(vrr_bugfix_dropped_frame, "")
+FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine, "debug.renderengine.graphite")
+FLAG_MANAGER_ACONFIG_FLAG(filter_frames_before_trace_starts, "")
+FLAG_MANAGER_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
+FLAG_MANAGER_ACONFIG_FLAG(deprecate_vsync_sf, "");
+FLAG_MANAGER_ACONFIG_FLAG(allow_n_vsyncs_in_targeter, "");
+FLAG_MANAGER_ACONFIG_FLAG(detached_mirror, "");
+FLAG_MANAGER_ACONFIG_FLAG(commit_not_composited, "");
+FLAG_MANAGER_ACONFIG_FLAG(correct_dpi_with_display_size, "");
+FLAG_MANAGER_ACONFIG_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(override_trusted_overlay, "");
+FLAG_MANAGER_ACONFIG_FLAG(flush_buffer_slots_to_uncache, "");
+FLAG_MANAGER_ACONFIG_FLAG(force_compile_graphite_renderengine, "");
+FLAG_MANAGER_ACONFIG_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots");
+FLAG_MANAGER_ACONFIG_FLAG(display_config_error_hal, "");
+FLAG_MANAGER_ACONFIG_FLAG(connected_display_hdr, "debug.sf.connected_display_hdr");
+FLAG_MANAGER_ACONFIG_FLAG(deprecate_frame_tracker, "");
+FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
+FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
+FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
 
-/// Trunk stable server flags ///
-FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
-FLAG_MANAGER_SERVER_FLAG(adpf_gpu_sf, "")
+/// Trunk stable server (R/W) flags ///
+FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")
+FLAG_MANAGER_ACONFIG_FLAG(adpf_gpu_sf, "")
+FLAG_MANAGER_ACONFIG_FLAG(adpf_native_session_manager, "");
+FLAG_MANAGER_ACONFIG_FLAG(graphite_renderengine_preview_rollout, "");
 
-/// Trunk stable server flags from outside SurfaceFlinger ///
-FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+/// Trunk stable server (R/W) flags from outside SurfaceFlinger ///
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
 
 /// 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)
-FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(trace_frame_rate_override, "",
-                                     com::android::graphics::libgui::flags);
-
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
+                                   com::android::server::display::feature::flags)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(trace_frame_rate_override, "",
+                                   com::android::graphics::libgui::flags);
+FLAG_MANAGER_ACONFIG_FLAG_IMPORTED(luts_api, "",
+                                   android::hardware::flags);
 } // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index a1be194..d8887f5 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -47,19 +47,23 @@
     bool use_adpf_cpu_hint() const;
     bool use_skia_tracing() const;
 
-    /// Trunk stable server flags ///
+    /// Trunk stable server (R/W) flags ///
     bool refresh_rate_overlay_on_external_display() const;
     bool adpf_gpu_sf() const;
     bool adpf_use_fmq_channel() const;
+    bool adpf_native_session_manager() const;
     bool adpf_use_fmq_channel_fixed() const;
+    bool graphite_renderengine_preview_rollout() const;
 
     /// Trunk stable readonly flags ///
+    bool arr_setframerate_gte_enum() const;
+    bool adpf_fmq_sf() const;
     bool connected_display() const;
     bool frame_rate_category_mrr() const;
     bool enable_small_area_detection() const;
+    bool stable_edid_ids() const;
     bool misc1() const;
     bool vrr_config() const;
-    bool hotplug2() const;
     bool hdcp_level_hal() const;
     bool multithreaded_present() const;
     bool add_sf_skipped_frames_to_trace() const;
@@ -70,15 +74,14 @@
     bool fp16_client_target() const;
     bool game_default_frame_rate() const;
     bool enable_layer_command_batching() const;
-    bool screenshot_fence_preservation() const;
     bool vulkan_renderengine() const;
     bool vrr_bugfix_24q4() const;
     bool vrr_bugfix_dropped_frame() const;
     bool renderable_buffer_usage() const;
     bool restore_blur_step() const;
     bool dont_skip_on_early_ro() const;
+    bool no_vsyncs_on_screen_off() const;
     bool protected_if_client() const;
-    bool ce_fence_promise() const;
     bool idle_screen_refresh_rate_timeout() const;
     bool graphite_renderengine() const;
     bool filter_frames_before_trace_starts() const;
@@ -92,9 +95,15 @@
     bool override_trusted_overlay() const;
     bool flush_buffer_slots_to_uncache() const;
     bool force_compile_graphite_renderengine() const;
-    bool single_hop_screenshot() const;
     bool trace_frame_rate_override() const;
     bool true_hdr_screenshots() const;
+    bool display_config_error_hal() const;
+    bool connected_display_hdr() const;
+    bool deprecate_frame_tracker() const;
+    bool skip_invisible_windows_in_input() const;
+    bool begone_bright_hlg() const;
+    bool luts_api() const;
+    bool window_blur_kawase2() const;
 
 protected:
     // overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index ae502cf..1de6b4a 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -28,16 +28,18 @@
 cc_defaults {
     name: "surfaceflinger_fuzz_defaults",
     static_libs: [
-        "libc++fs",
         "libsurfaceflinger_common",
     ],
     srcs: [
+        ":libsurfaceflinger_backend_mock_sources",
+        ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
     ],
     defaults: [
         "libsurfaceflinger_defaults",
     ],
     header_libs: [
+        "libsurfaceflinger_backend_mock_headers",
         "libsurfaceflinger_headers",
     ],
     cflags: [
diff --git a/services/surfaceflinger/EventLog/EventLogTags.logtags b/services/surfaceflinger/surfaceflinger.logtags
similarity index 88%
rename from services/surfaceflinger/EventLog/EventLogTags.logtags
rename to services/surfaceflinger/surfaceflinger.logtags
index 6c851dd..6dbe0dd 100644
--- a/services/surfaceflinger/EventLog/EventLogTags.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -31,11 +31,10 @@
 # 6: Percent
 # Default value for data of type int/long is 2 (bytes).
 #
-# See system/core/logcat/event.logtags for the master copy of the tags.
+# See system/logging/logcat/event.logtags for the master copy of the tags.
 
 # 60100 - 60199 reserved for surfaceflinger
 
-60100 sf_frame_dur (window|3),(dur0|1),(dur1|1),(dur2|1),(dur3|1),(dur4|1),(dur5|1),(dur6|1)
 60110 sf_stop_bootanim (time|2|3)
 
 # NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 102e2b6..bdd826d 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -4,6 +4,14 @@
 container: "system"
 
 flag {
+  name: "adpf_fmq_sf"
+  namespace: "game"
+  description: "Guards use of the ADPF FMQ system specifically for SurfaceFlinger"
+  bug: "315894228"
+  is_fixed_read_only: true
+} # adpf_fmq_sf
+
+flag {
   name: "adpf_gpu_sf"
   namespace: "game"
   description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
@@ -11,6 +19,47 @@
 } # adpf_gpu_sf
 
 flag {
+  name: "adpf_native_session_manager"
+  namespace: "game"
+  description: "Controls ADPF SessionManager being enabled in SF"
+  bug: "367803904"
+} # adpf_sessionmanager
+
+flag {
+  name: "arr_setframerate_api"
+  namespace: "core_graphics"
+  description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16"
+  bug: "356987016"
+  is_fixed_read_only: true
+  is_exported: true
+} # arr_setframerate_api
+
+flag {
+  name: "arr_setframerate_gte_enum"
+  namespace: "core_graphics"
+  description: "Exposes GTE (greater than or equal to) enum for Android 16"
+  bug: "380949716"
+  is_fixed_read_only: true
+} # arr_setframerate_gte_enum
+
+flag {
+  name: "arr_surfacecontrol_setframerate_api"
+  namespace: "core_graphics"
+  description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16"
+  bug: "356987016"
+  is_fixed_read_only: true
+  is_exported: true
+} # arr_surfacecontrol_setframerate_api
+
+flag {
+  name: "begone_bright_hlg"
+  namespace: "core_graphics"
+  description: "Caps HLG brightness relative to SDR"
+  bug: "362510107"
+  is_fixed_read_only: true
+} # begone_bright_hlg
+
+flag {
   name: "ce_fence_promise"
   namespace: "window_surfaces"
   description: "Moves logic for buffer release fences into LayerFE"
@@ -33,6 +82,14 @@
 } # commit_not_composited
 
 flag {
+  name: "connected_display_hdr"
+  namespace: "core_graphics"
+  description: "enable connected display hdr capability"
+  bug: "374182788"
+  is_fixed_read_only: true
+} # connected_display_hdr
+
+flag {
   name: "correct_dpi_with_display_size"
   namespace: "core_graphics"
   description: "indicate whether missing or likely incorrect dpi should be corrected using the display size."
@@ -44,6 +101,17 @@
 } # correct_dpi_with_display_size
 
 flag {
+  name: "deprecate_frame_tracker"
+  namespace: "core_graphics"
+  description: "Deprecate using FrameTracker to accumulate and provide FrameStats"
+  bug: "241394120"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+} # deprecate_frame_tracker
+
+flag {
   name: "deprecate_vsync_sf"
   namespace: "core_graphics"
   description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
@@ -66,6 +134,14 @@
 } # detached_mirror
 
 flag {
+  name: "display_config_error_hal"
+  namespace: "core_graphics"
+  description: "Report HAL display configuration errors like modeset failure or link training failure"
+  bug: "374184110"
+  is_fixed_read_only: true
+} # display_config_error_hal
+
+flag {
   name: "filter_frames_before_trace_starts"
   namespace: "core_graphics"
   description: "Do not trace FrameTimeline events for frames started before the trace started"
@@ -107,6 +183,13 @@
 } # frame_rate_category_mrr
 
 flag {
+  name: "graphite_renderengine_preview_rollout"
+  namespace: "core_graphics"
+  description: "R/W flag to enable Skia's Graphite Vulkan backend in RenderEngine, IF it is already compiled with force_compile_graphite_renderengine, AND the debug.renderengine.graphite_preview_optin sysprop is set to true."
+  bug: "293371537"
+} # graphite_renderengine_preview_rollout
+
+flag {
   name: "latch_unsignaled_with_auto_refresh_changed"
   namespace: "core_graphics"
   description: "Ignore eAutoRefreshChanged with latch unsignaled"
@@ -126,6 +209,14 @@
 } # local_tonemap_screenshots
 
 flag {
+  name: "no_vsyncs_on_screen_off"
+  namespace: "core_graphics"
+  description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
+  bug: "331636736"
+  is_fixed_read_only: true
+} # no_vsyncs_on_screen_off
+
+flag {
   name: "single_hop_screenshot"
   namespace: "window_surfaces"
   description: "Only access SF main thread once during a screenshot"
@@ -137,6 +228,25 @@
  } # single_hop_screenshot
 
 flag {
+  name: "skip_invisible_windows_in_input"
+  namespace: "window_surfaces"
+  description: "Only send visible windows to input list"
+  bug: "305254099"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+ } # skip_invisible_windows_in_input
+
+flag {
+  name: "stable_edid_ids"
+  namespace: "core_graphics"
+  description: "Guard use of the new stable EDID-based display IDs system."
+  bug: "352320847"
+  is_fixed_read_only: true
+} # stable_edid_ids
+
+flag {
   name: "true_hdr_screenshots"
   namespace: "core_graphics"
   description: "Enables screenshotting display content in HDR, sans tone mapping"
@@ -185,4 +295,11 @@
   }
 } # vrr_bugfix_dropped_frame
 
+flag {
+  name: "window_blur_kawase2"
+  namespace: "core_graphics"
+  description: "Flag for using Kawase2 algorithm for window blur"
+  bug: "353826438"
+} # window_blur_kawase2
+
 # IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index e6fed63..7b6e4bf 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -341,9 +341,9 @@
     WindowInfosListenerUtils windowInfosListenerUtils;
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                            ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -370,7 +370,8 @@
         UIDFaker f(AID_SYSTEM);
         auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
             auto foundWindowInfo =
-                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+                    WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+                                                                     windowInfos);
             if (!foundWindowInfo) {
                 return false;
             }
@@ -386,7 +387,8 @@
         Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true);
         auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
             auto foundWindowInfo =
-                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+                    WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(),
+                                                                     windowInfos);
             if (!foundWindowInfo) {
                 return false;
             }
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 56cf13d..65add63 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -19,6 +19,7 @@
 #pragma clang diagnostic ignored "-Wconversion"
 
 #include <common/FlagManager.h>
+#include <gui/IConsumerListener.h>
 #include <ui/DisplayState.h>
 
 #include "LayerTransactionTest.h"
@@ -45,11 +46,17 @@
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
         SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
-                                       mMainDisplayMode.resolution.getHeight());
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mConsumer->setConsumerName(String8("Virtual disp consumer (MultiDisplayLayerBounds)"));
+        mConsumer->setDefaultBufferSize(mMainDisplayMode.resolution.getWidth(),
+                                        mMainDisplayMode.resolution.getHeight());
+
+        class StubConsumerListener : public BnConsumerListener {
+            virtual void onFrameAvailable(const BufferItem&) override {}
+            virtual void onBuffersReleased() override {}
+            virtual void onSidebandStreamChanged() override {}
+        };
+        mConsumer->consumerConnect(sp<StubConsumerListener>::make(), true);
     }
 
     virtual void TearDown() {
@@ -92,6 +99,7 @@
     sp<IBinder> mMainDisplay;
     PhysicalDisplayId mMainDisplayId;
     sp<IBinder> mVirtualDisplay;
+    sp<IGraphicBufferConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
     sp<SurfaceControl> mColorLayer;
     Color mExpectedColor = {63, 63, 195, 255};
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 67a5247..c95c875 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -17,7 +17,6 @@
 #define ANDROID_TRANSACTION_TEST_HARNESSES
 
 #include <com_android_graphics_libgui_flags.h>
-#include <common/FlagManager.h>
 #include <ui/DisplayState.h>
 
 #include "LayerTransactionTest.h"
@@ -59,7 +58,7 @@
                         GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN);
                 sp<BufferListener> listener = sp<BufferListener>::make(this);
                 itemConsumer->setFrameAvailableListener(listener);
-                itemConsumer->setName(String8("Virtual disp consumer"));
+                itemConsumer->setName(String8("Virtual disp consumer (TransactionTest)"));
                 itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 #else
                 sp<IGraphicBufferProducer> producer;
@@ -67,7 +66,7 @@
                 sp<BufferItemConsumer> itemConsumer;
                 BufferQueue::createBufferQueue(&producer, &consumer);
 
-                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setConsumerName(String8("Virtual disp consumer (TransactionTest)"));
                 consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 
                 itemConsumer = sp<BufferItemConsumer>::make(consumer,
@@ -96,12 +95,8 @@
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
                 t.setDisplayProjection(vDisplay, displayState.orientation,
                                        Rect(displayState.layerStackSpaceRect), Rect(resolution));
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    t.setDisplayLayerStack(vDisplay, layerStack);
-                    t.setLayerStack(mirrorSc, layerStack);
-                } else {
-                    t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
-                }
+                t.setDisplayLayerStack(vDisplay, layerStack);
+                t.setLayerStack(mirrorSc, layerStack);
                 t.apply();
                 SurfaceComposerClient::Transaction().apply(true);
 
@@ -121,10 +116,8 @@
                 // CompositionEngine::present may attempt to be called on the same
                 // display multiple times. The layerStack is set to invalid here so
                 // that the display is ignored if that scenario occurs.
-                if (FlagManager::getInstance().ce_fence_promise()) {
-                    t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
-                    t.apply(true);
-                }
+                t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
+                t.apply(true);
                 SurfaceComposerClient::destroyVirtualDisplay(vDisplay);
                 return sc;
         }
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index d69378c..1108c7f 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -29,14 +29,14 @@
     void SetUp() override {
 #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
         mGLConsumer = sp<GLConsumer>::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false);
-        mGLConsumer->setName(String8("Virtual disp consumer"));
+        mGLConsumer->setName(String8("Virtual disp consumer (VirtualDisplayTest)"));
         mGLConsumer->setDefaultBufferSize(100, 100);
         mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer();
 #else
         sp<IGraphicBufferConsumer> consumer;
 
         BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setConsumerName(String8("Virtual disp consumer (VirtualDisplayTest)"));
         consumer->setDefaultBufferSize(100, 100);
 
         mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index ad9a674..2dd0dd9 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -50,9 +50,9 @@
 TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -65,14 +65,14 @@
             .apply();
 
     auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return findMatchingWindowInfo(windowInfo, windowInfos);
+        return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
 
     Transaction().reparent(surfaceControl, nullptr).apply();
 
     auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
-        return !findMatchingWindowInfo(windowInfo, windowInfos);
+        return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
 }
@@ -80,9 +80,9 @@
 TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
     std::string name = "Test Layer";
     sp<IBinder> token = sp<BBinder>::make();
-    WindowInfo windowInfo;
-    windowInfo.name = name;
-    windowInfo.token = token;
+    auto windowInfo = sp<gui::WindowInfoHandle>::make();
+    windowInfo->editInfo()->name = name;
+    windowInfo->editInfo()->token = token;
     sp<SurfaceControl> surfaceControl =
             mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceBufferState);
@@ -96,7 +96,7 @@
             .apply();
 
     auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
-        auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+        auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
         if (!foundWindowInfo) {
             return false;
         }
@@ -104,19 +104,19 @@
     };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
 
-    windowInfo.addTouchableRegion({0, 0, 50, 50});
+    windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50});
     Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
 
     auto windowIsPresentAndTouchableRegionMatches =
             [&](const std::vector<WindowInfo>& windowInfos) {
-                auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+                auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos);
                 if (!foundWindowInfo) {
                     return false;
                 }
 
                 auto touchableRegion =
                         foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion);
-                return touchableRegion.hasSameRects(windowInfo.touchableRegion);
+                return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion);
             };
     ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
 }
diff --git a/services/surfaceflinger/tests/benchmarks/Android.bp b/services/surfaceflinger/tests/benchmarks/Android.bp
index 1c47be34..22fca08 100644
--- a/services/surfaceflinger/tests/benchmarks/Android.bp
+++ b/services/surfaceflinger/tests/benchmarks/Android.bp
@@ -22,7 +22,6 @@
     static_libs: [
         "libgmock",
         "libgtest",
-        "libc++fs",
     ],
     header_libs: [
         "libsurfaceflinger_mocks_headers",
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index ae380ad..9794620 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -182,7 +182,7 @@
         mLifecycleManager.applyTransactions(setZTransaction(id, z));
     }
 
-    void setCrop(uint32_t id, const Rect& crop) {
+    void setCrop(uint32_t id, const FloatRect& crop) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
         transactions.back().states.push_back({});
@@ -193,6 +193,8 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); }
+
     void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -216,6 +218,17 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setAutoRefresh(uint32_t id, bool autoRefresh) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eAutoRefreshChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.autoRefresh = autoRefresh;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void hideLayer(uint32_t id) {
         setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
     }
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index bce1406..6eb7f4a 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -35,9 +35,6 @@
         ":libsurfaceflinger_mock_sources",
         "TransactionTraceTestSuite.cpp",
     ],
-    static_libs: [
-        "libc++fs",
-    ],
     header_libs: [
         "libsurfaceflinger_mocks_headers",
     ],
diff --git a/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
new file mode 100644
index 0000000..b926d2f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActivePictureUpdaterTest.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/gui/ActivePicture.h>
+#include <android/gui/IActivePictureListener.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <mock/DisplayHardware/MockComposer.h>
+#include <mock/MockLayer.h>
+#include <renderengine/mock/RenderEngine.h>
+
+#include "ActivePictureUpdater.h"
+#include "LayerFE.h"
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using android::compositionengine::LayerFECompositionState;
+using android::gui::ActivePicture;
+using android::gui::IActivePictureListener;
+using android::mock::MockLayer;
+using surfaceflinger::frontend::LayerSnapshot;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+class TestableLayerFE : public LayerFE {
+public:
+    TestableLayerFE() : LayerFE("TestableLayerFE"), snapshot(*(new LayerSnapshot)) {
+        mSnapshot = std::unique_ptr<LayerSnapshot>(&snapshot);
+    }
+
+    LayerSnapshot& snapshot;
+};
+
+class ActivePictureUpdaterTest : public testing::Test {
+protected:
+    SurfaceFlinger* flinger() {
+        if (!mFlingerSetup) {
+            mFlinger.setupMockScheduler();
+            mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+            mFlinger.setupRenderEngine(std::make_unique<renderengine::mock::RenderEngine>());
+            mFlingerSetup = true;
+        }
+        return mFlinger.flinger();
+    }
+
+private:
+    TestableSurfaceFlinger mFlinger;
+    bool mFlingerSetup = false;
+};
+
+// Hack to workaround initializer lists not working for parcelables because parcelables inherit from
+// Parcelable, which has a virtual destructor.
+auto UnorderedElementsAre(std::initializer_list<std::tuple<int32_t, int32_t, int64_t>> tuples) {
+    std::vector<ActivePicture> activePictures;
+    for (auto tuple : tuples) {
+        ActivePicture ap;
+        ap.layerId = std::get<0>(tuple);
+        ap.ownerUid = std::get<1>(tuple);
+        ap.pictureProfileId = std::get<2>(tuple);
+        activePictures.push_back(ap);
+    }
+    return testing::UnorderedElementsAreArray(activePictures);
+}
+
+// Parcelables don't define this for matchers, which is unfortunate
+void PrintTo(const ActivePicture& activePicture, std::ostream* os) {
+    *os << activePicture.toString();
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWithNoProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStartsUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenLayerContinuesUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerStopsUsingProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle::NONE;
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenLayerChangesProfile) {
+    sp<NiceMock<MockLayer>> layer = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE;
+    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer, layerFE, layerFE.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 2}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, notCalledWhenUncommittedLayerChangesProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_FALSE(updater.updateAndHasChanged());
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenDifferentLayerUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(20)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 20, 2}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 2}, {200, 20, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenSameUidUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 2}}));
+    }
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(2);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 2}, {200, 10, 1}}));
+    }
+}
+
+TEST_F(ActivePictureUpdaterTest, calledWhenNewLayerUsesSameProfile) {
+    sp<NiceMock<MockLayer>> layer1 = sp<NiceMock<MockLayer>>::make(flinger(), 100);
+    TestableLayerFE layerFE1;
+    EXPECT_CALL(*layer1, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    ActivePictureUpdater updater;
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(), UnorderedElementsAre({{100, 10, 1}}));
+    }
+
+    sp<NiceMock<MockLayer>> layer2 = sp<NiceMock<MockLayer>>::make(flinger(), 200);
+    TestableLayerFE layerFE2;
+    EXPECT_CALL(*layer2, getOwnerUid()).WillRepeatedly(Return(uid_t(10)));
+
+    {
+        layerFE1.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE1.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer1, layerFE1, layerFE1.stealCompositionResult());
+
+        layerFE2.snapshot.pictureProfileHandle = PictureProfileHandle(1);
+        layerFE2.onPictureProfileCommitted();
+        updater.onLayerComposed(*layer2, layerFE2, layerFE2.stealCompositionResult());
+
+        ASSERT_TRUE(updater.updateAndHasChanged());
+        EXPECT_THAT(updater.getActivePictures(),
+                    UnorderedElementsAre({{100, 10, 1}, {200, 10, 1}}));
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f1bd87c..6af5143 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -22,15 +22,44 @@
     default_team: "trendy_team_android_core_graphics_stack",
 }
 
+// This is a step towards pulling out the "backend" sources to clean up the
+// dependency graph between CompositionEngine and SurfaceFlinger.
+// MockNativeWindow doesn't strictly belong here, but this works for now so
+// that CompositionEngine tests can use these mocks.
+filegroup {
+    name: "libsurfaceflinger_backend_mock_sources",
+    srcs: [
+        ":poweradvisor_mock_sources",
+        "mock/DisplayHardware/MockComposer.cpp",
+        "mock/DisplayHardware/MockHWC2.cpp",
+        "mock/DisplayHardware/MockHWComposer.cpp",
+        "mock/system/window/MockNativeWindow.cpp",
+    ],
+}
+
+cc_library_headers {
+    name: "libsurfaceflinger_backend_mock_headers",
+    export_include_dirs: ["."],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    export_static_lib_headers: [
+        "libgmock",
+        "libgtest",
+    ],
+}
+
+filegroup {
+    name: "poweradvisor_mock_sources",
+    srcs: [
+        "mock/PowerAdvisor/*.cpp",
+    ],
+}
+
 filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
-        "mock/DisplayHardware/MockPowerHalController.cpp",
-        "mock/DisplayHardware/MockComposer.cpp",
-        "mock/DisplayHardware/MockHWC2.cpp",
-        "mock/DisplayHardware/MockIPower.cpp",
-        "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
-        "mock/DisplayHardware/MockPowerAdvisor.cpp",
         "mock/MockEventThread.cpp",
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
@@ -39,7 +68,6 @@
         "mock/MockVsyncController.cpp",
         "mock/MockVSyncDispatch.cpp",
         "mock/MockVSyncTracker.cpp",
-        "mock/system/window/MockNativeWindow.cpp",
     ],
 }
 
@@ -57,84 +85,12 @@
         "surfaceflinger_defaults",
     ],
     test_suites: ["device-tests"],
-    static_libs: ["libc++fs"],
     header_libs: ["surfaceflinger_tests_common_headers"],
     srcs: [
+        ":libsurfaceflinger_backend_mock_sources",
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
-        "libsurfaceflinger_unittest_main.cpp",
-        "ActiveDisplayRotationFlagsTest.cpp",
-        "BackgroundExecutorTest.cpp",
-        "CommitTest.cpp",
-        "CompositionTest.cpp",
-        "DaltonizerTest.cpp",
-        "DisplayIdGeneratorTest.cpp",
-        "DisplayTransactionTest.cpp",
-        "DisplayDevice_GetBestColorModeTest.cpp",
-        "DisplayDevice_SetDisplayBrightnessTest.cpp",
-        "DisplayDevice_SetProjectionTest.cpp",
-        "DisplayModeControllerTest.cpp",
-        "EventThreadTest.cpp",
-        "FlagManagerTest.cpp",
-        "FpsReporterTest.cpp",
-        "FpsTest.cpp",
-        "FramebufferSurfaceTest.cpp",
-        "FrameRateOverrideMappingsTest.cpp",
-        "FrameTimelineTest.cpp",
-        "HWComposerTest.cpp",
-        "JankTrackerTest.cpp",
-        "OneShotTimerTest.cpp",
-        "LayerHistoryIntegrationTest.cpp",
-        "LayerInfoTest.cpp",
-        "LayerMetadataTest.cpp",
-        "LayerHierarchyTest.cpp",
-        "LayerLifecycleManagerTest.cpp",
-        "LayerSnapshotTest.cpp",
-        "LayerTestUtils.cpp",
-        "MessageQueueTest.cpp",
-        "PowerAdvisorTest.cpp",
-        "SmallAreaDetectionAllowMappingsTest.cpp",
-        "SurfaceFlinger_ColorMatrixTest.cpp",
-        "SurfaceFlinger_CreateDisplayTest.cpp",
-        "SurfaceFlinger_DestroyDisplayTest.cpp",
-        "SurfaceFlinger_DisplayModeSwitching.cpp",
-        "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
-        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
-        "SurfaceFlinger_FoldableTest.cpp",
-        "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
-        "SurfaceFlinger_GetDisplayStatsTest.cpp",
-        "SurfaceFlinger_HdrOutputControlTest.cpp",
-        "SurfaceFlinger_HotplugTest.cpp",
-        "SurfaceFlinger_InitializeDisplaysTest.cpp",
-        "SurfaceFlinger_NotifyExpectedPresentTest.cpp",
-        "SurfaceFlinger_NotifyPowerBoostTest.cpp",
-        "SurfaceFlinger_PowerHintTest.cpp",
-        "SurfaceFlinger_SetDisplayStateTest.cpp",
-        "SurfaceFlinger_SetPowerModeInternalTest.cpp",
-        "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
-        "SchedulerTest.cpp",
-        "RefreshRateSelectorTest.cpp",
-        "RefreshRateStatsTest.cpp",
-        "RegionSamplingTest.cpp",
-        "TestableScheduler.cpp",
-        "TimeStatsTest.cpp",
-        "FrameTracerTest.cpp",
-        "TransactionApplicationTest.cpp",
-        "TransactionFrameTracerTest.cpp",
-        "TransactionProtoParserTest.cpp",
-        "TransactionSurfaceFrameTest.cpp",
-        "TransactionTraceWriterTest.cpp",
-        "TransactionTracingTest.cpp",
-        "TunnelModeEnabledReporterTest.cpp",
-        "VSyncCallbackRegistrationTest.cpp",
-        "VSyncDispatchTimerQueueTest.cpp",
-        "VSyncDispatchRealtimeTest.cpp",
-        "VsyncModulatorTest.cpp",
-        "VSyncPredictorTest.cpp",
-        "VSyncReactorTest.cpp",
-        "VsyncConfigurationTest.cpp",
-        "VsyncScheduleTest.cpp",
-        "WindowInfosListenerInvokerTest.cpp",
+        "*.cpp",
     ],
 }
 
@@ -199,6 +155,7 @@
         "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog_surfaceflinger",
         "libSurfaceFlingerProp",
         "libsync",
         "libui",
diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
index 5413bae..72d1351 100644
--- a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #include <gtest/gtest.h>
 #include <condition_variable>
 
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index d4c801f..b517ff0 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -22,8 +22,8 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockTimeStats.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -33,11 +33,11 @@
     void SetUp() override {
         mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
         mComposer = new Hwc2::mock::Composer();
-        mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+        mPowerAdvisor = new adpf::mock::PowerAdvisor();
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
         mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-        mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+        mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
 
         constexpr bool kIsPrimary = true;
         FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -79,7 +79,7 @@
             sp<compositionengine::mock::DisplaySurface>::make();
     sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
-    Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
     Hwc2::mock::Composer* mComposer = nullptr;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 23d3c16..860ad2e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -40,10 +40,10 @@
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockTimeStats.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -110,9 +110,9 @@
         mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
 
         mComposer = new Hwc2::mock::Composer();
-        mPowerAdvisor = new Hwc2::mock::PowerAdvisor();
+        mPowerAdvisor = new adpf::mock::PowerAdvisor();
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-        mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
+        mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor));
         mFlinger.mutableMaxRenderTargetSize() = 16384;
     }
 
@@ -158,7 +158,7 @@
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
-    Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+    adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr;
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
@@ -467,7 +467,7 @@
                                                          LayerProperties::FORMAT,
                                                          LayerProperties::USAGE |
                                                                  GraphicBuffer::USAGE_HW_TEXTURE);
-        layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         layer.externalTexture = buffer;
         layer.bufferData->acquireFence = Fence::NO_FENCE;
         layer.dataspace = ui::Dataspace::UNKNOWN;
@@ -664,7 +664,8 @@
                 NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                      false);
         layer.sidebandStream = stream;
-        layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
+        layer.crop =
+                FloatRect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -828,7 +829,7 @@
             return frontend::RequestedLayerState(args);
         });
 
-        layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
         return layer;
     }
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
index d971150..29a1fab 100644
--- a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -20,6 +20,7 @@
 #include "Display/DisplayModeController.h"
 #include "Display/DisplaySnapshot.h"
 #include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/Hal.h"
 #include "DisplayIdentificationTestHelpers.h"
 #include "FpsOps.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -103,7 +104,7 @@
 
         EXPECT_CALL(*mComposerHal,
                     setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
-                .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+                .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)));
 
         return constraints;
     }
@@ -183,7 +184,8 @@
     hal::VsyncPeriodChangeTimeline timeline;
     const auto constraints = expectModeSet(modeRequest, timeline);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
 
     mDmc.clearDesiredMode(mDisplayId);
@@ -210,7 +212,8 @@
     hal::VsyncPeriodChangeTimeline timeline;
     auto constraints = expectModeSet(modeRequest, timeline);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
 
     // No action since a mode switch has already been initiated.
@@ -223,7 +226,8 @@
     constexpr bool kSubsequent = true;
     constraints = expectModeSet(modeRequest, timeline, kSubsequent);
 
-    EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+    EXPECT_EQ(DisplayModeController::ModeChangeResult::Changed,
+              mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
     EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
 
     mDmc.clearDesiredMode(mDisplayId);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index db3c0a1..fa976c8 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,10 +47,10 @@
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -118,7 +118,7 @@
     sp<GraphicBuffer> mBuffer =
             sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
                                     GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
-    Hwc2::mock::PowerAdvisor mPowerAdvisor;
+    adpf::mock::PowerAdvisor mPowerAdvisor;
 
     FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
 
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 625d2e6..268a6c4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -23,6 +23,7 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/DisplayEventReceiver.h>
 #include <log/log.h>
 #include <scheduler/VsyncConfig.h>
 #include <utils/Errors.h>
@@ -111,6 +112,8 @@
     void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime);
     void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                             std::vector<FrameRateOverride>);
+    void expectQueuedBufferCountReceivedByConnection(
+            ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount);
 
     void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
                       nsecs_t deadlineTimestamp) {
@@ -144,6 +147,7 @@
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
+    std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders;
 
     std::chrono::nanoseconds mVsyncPeriod;
 
@@ -376,6 +380,14 @@
     EXPECT_EQ(expectedDisplayId, event.header.displayId);
 }
 
+void EventThreadTest::expectQueuedBufferCountReceivedByConnection(
+        ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) {
+    auto args = connectionEventRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value());
+    const auto& event = std::get<0>(args.value());
+    EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers);
+}
+
 namespace {
 
 using namespace testing;
@@ -868,6 +880,63 @@
     EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel);
 }
 
+TEST_F(EventThreadTest, connectionReceivesBufferStuffing) {
+    setupEventThread();
+
+    // Create a connection that will experience buffer stuffing.
+    ConnectionEventRecorder stuffedConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> stuffedConnection =
+            createConnection(stuffedConnectionEventRecorder,
+                             gui::ISurfaceComposer::EventRegistration::modeChanged |
+                                     gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+                             111);
+
+    // Add a connection and buffer count to the list of stuffed Uids that will receive
+    // data in the next vsync event.
+    BufferStuffingMap bufferStuffedUids;
+    bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3);
+    mThread->addBufferStuffedUids(bufferStuffedUids);
+    mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder);
+
+    // Signal that we want the next vsync event to be posted to two connections.
+    mThread->requestNextVsync(mConnection);
+    mThread->requestNextVsync(stuffedConnection);
+    onVSyncEvent(123, 456, 789);
+
+    // Vsync event data contains number of queued buffers.
+    expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0);
+    expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3);
+}
+
+TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) {
+    setupEventThread();
+
+    // Create a connection with the same Uid as another connection.
+    ConnectionEventRecorder secondConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> secondConnection =
+            createConnection(secondConnectionEventRecorder,
+                             gui::ISurfaceComposer::EventRegistration::modeChanged |
+                                     gui::ISurfaceComposer::EventRegistration::frameRateOverride,
+                             mConnectionUid);
+
+    // Add connection Uid and buffer count to the list of stuffed Uids that will receive
+    // data in the next vsync event.
+    BufferStuffingMap bufferStuffedUids;
+    bufferStuffedUids.try_emplace(mConnectionUid, 3);
+    mThread->addBufferStuffedUids(bufferStuffedUids);
+    mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder);
+    mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder);
+
+    // Signal that we want the next vsync event to be posted to two connections.
+    mThread->requestNextVsync(mConnection);
+    mThread->requestNextVsync(secondConnection);
+    onVSyncEvent(123, 456, 789);
+
+    // Vsync event data contains number of queued buffers.
+    expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3);
+    expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3);
+}
+
 } // namespace
 } // namespace android
 
diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
index 6e4bf2b..744c536 100644
--- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
+++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h
@@ -19,14 +19,14 @@
 #include <gmock/gmock.h>
 
 #include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using android::adpf::mock::PowerAdvisor;
 using android::hardware::graphics::composer::hal::HWDisplayId;
-using android::Hwc2::mock::PowerAdvisor;
 
 struct FakeDisplayInjectorArgs {
     PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u);
@@ -36,7 +36,7 @@
 
 class FakeDisplayInjector {
 public:
-    FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor,
+    FakeDisplayInjector(TestableSurfaceFlinger& flinger, PowerAdvisor& powerAdvisor,
                         sp<mock::NativeWindow> nativeWindow)
           : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {}
 
@@ -89,7 +89,7 @@
     }
 
     TestableSurfaceFlinger& mFlinger;
-    Hwc2::mock::PowerAdvisor& mPowerAdvisor;
+    PowerAdvisor& mPowerAdvisor;
     sp<mock::NativeWindow> mNativeWindow;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 51b5f40..a5b347a 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -85,12 +85,6 @@
     EXPECT_EQ(false, mFlagManager.test_flag());
 }
 
-TEST_F(FlagManagerTest, crashesIfQueriedBeforeBoot) {
-    mFlagManager.markBootIncomplete();
-    EXPECT_DEATH(FlagManager::getInstance()
-        .refresh_rate_overlay_on_external_display(), "");
-}
-
 TEST_F(FlagManagerTest, returnsOverrideTrue) {
     mFlagManager.markBootCompleted();
 
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 0dfbd61..08e4265 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -166,6 +166,7 @@
                 a.presentTime == b.presentTime;
     }
 
+    NO_THREAD_SAFETY_ANALYSIS
     const std::map<int64_t, TimelineItem>& getPredictions() const {
         return mTokenManager->mPredictions;
     }
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index e0753a3..ba2d3e2 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -69,7 +69,7 @@
 using ::testing::StrictMock;
 
 struct HWComposerTest : testing::Test {
-    using HalError = hardware::graphics::composer::V2_1::Error;
+    using HalError = hal::Error;
 
     Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
     impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
@@ -384,6 +384,7 @@
         const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
         const float expectedDpiX = (kWidth * kMmPerInch / size.width);
         const float expectedDpiY = (kHeight * kMmPerInch / size.height);
+        const OutputType hdrOutputType = OutputType::SYSTEM;
         const hal::VrrConfig vrrConfig =
                 hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
                                .notifyExpectedPresentConfig = hal::VrrConfig::
@@ -394,7 +395,8 @@
                                                        .height = kHeight,
                                                        .configGroup = kConfigGroup,
                                                        .vsyncPeriod = kVsyncPeriod,
-                                                       .vrrConfig = vrrConfig};
+                                                       .vrrConfig = vrrConfig,
+                                                       .hdrOutputType = hdrOutputType};
 
         EXPECT_CALL(*mHal, getDisplayConfigurations(kHwcDisplayId, _, _))
                 .WillOnce(DoAll(SetArgPointee<2>(std::vector<hal::DisplayConfiguration>{
@@ -410,6 +412,7 @@
         EXPECT_EQ(modes.front().configGroup, kConfigGroup);
         EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
         EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+        EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
         if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
             EXPECT_EQ(modes.front().dpiX, -1);
             EXPECT_EQ(modes.front().dpiY, -1);
@@ -435,6 +438,7 @@
         EXPECT_EQ(modes.front().configGroup, kConfigGroup);
         EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
         EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
+        EXPECT_EQ(modes.front().hdrOutputType, hdrOutputType);
         EXPECT_EQ(modes.front().dpiX, kDpi);
         EXPECT_EQ(modes.front().dpiY, kDpi);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index de37b63..53a9062 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -584,7 +584,7 @@
 
     auto layer = createLegacyAndFrontedEndLayer(1);
     showLayer(1);
-    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
     setFrameRateCategory(1, 0);
 
@@ -623,7 +623,7 @@
 
     auto layer = createLegacyAndFrontedEndLayer(1);
     showLayer(1);
-    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
                  ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
     setFrameRateCategory(1, 0);
 
@@ -654,6 +654,72 @@
     EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
 }
 
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_arr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // Set the test to be on a vrr mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_mrr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // True by default on MRR devices as well, but the device is not set to VRR mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, 0);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
 
@@ -715,6 +781,204 @@
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
+// Tests MRR NoPreference-only vote, no game default override. Expects vote reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setDefaultLayerVote(layer.get(), defaultVote);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(defaultVote, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Tests VRR NoPreference-only vote, no game default override. Expects NoPreference, *not* vote
+// reset.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const LayerHistory::LayerVoteType defaultVote = LayerHistory::LayerVoteType::Min;
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setDefaultLayerVote(layer.get(), defaultVote);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreferenceWithGameDefault_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(gameDefaultFrameRate, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_vrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Expect NoVote to be skipped in summarize.
+    EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVoteWithGameDefault_mrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const uid_t uid = 456;
+
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_NO_VOTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Expect NoVote to be skipped in summarize.
+    EXPECT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
 
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index c7cc21c..119e182 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -619,14 +619,32 @@
     }
 }
 
-TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) {
-    // By default the layer has no buffer, so it doesn't need an input info
-    EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
-
-    setBuffer(111);
+TEST_F(LayerLifecycleManagerTest, layerWithBufferNeedsInputInfo) {
+    // If a layer has no buffer or no color, it doesn't have an input info
+    LayerHierarchyTestBase::createRootLayer(3);
+    setColor(3, {-1._hf, -1._hf, -1._hf});
     mLifecycleManager.commitChanges();
 
-    EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+    EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo());
+
+    setBuffer(3);
+    mLifecycleManager.commitChanges();
+
+    EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 3)->needsInputInfo());
+}
+
+TEST_F(LayerLifecycleManagerTest, layerWithColorNeedsInputInfo) {
+    // If a layer has no buffer or no color, it doesn't have an input info
+    LayerHierarchyTestBase::createRootLayer(4);
+    setColor(4, {-1._hf, -1._hf, -1._hf});
+    mLifecycleManager.commitChanges();
+
+    EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo());
+
+    setColor(4, {1._hf, 0._hf, 0._hf});
+    mLifecycleManager.commitChanges();
+
+    EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 4)->needsInputInfo());
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 75d2fa3..bb54138 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -28,7 +28,6 @@
 #include "ui/GraphicTypes.h"
 
 #include <com_android_graphics_libgui_flags.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
 
 #define UPDATE_AND_VERIFY(BUILDER, ...)                                    \
     ({                                                                     \
@@ -162,12 +161,12 @@
     info.info.logicalHeight = 100;
     info.info.logicalWidth = 200;
     mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
-    Rect layerCrop(0, 0, 10, 20);
+    FloatRect layerCrop(0, 0, 10, 20);
     setCrop(11, layerCrop);
     EXPECT_TRUE(mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Geometry));
     UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(mSnapshotBuilder, STARTING_ZORDER);
     EXPECT_EQ(getSnapshot(11)->geomCrop, layerCrop);
-    EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop.toFloatRect());
+    EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop);
     float maxHeight = static_cast<float>(info.info.logicalHeight * 10);
     float maxWidth = static_cast<float>(info.info.logicalWidth * 10);
 
@@ -1551,6 +1550,9 @@
 }
 
 TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+                              skip_invisible_windows_in_input,
+                      false);
     LayerHierarchyTestBase::createRootLayer(3);
     setColor(3, {-1._hf, -1._hf, -1._hf});
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -1576,6 +1578,39 @@
     EXPECT_TRUE(foundInputLayer);
 }
 
+TEST_F(LayerSnapshotTest, NonVisibleLayerWithInputShouldNotBeIncluded) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+                              skip_invisible_windows_in_input,
+                      true);
+    LayerHierarchyTestBase::createRootLayer(3);
+    setColor(3, {-1._hf, -1._hf, -1._hf});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+    transactions.back().states.front().layerId = 3;
+    transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make();
+    auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+    inputInfo->token = sp<BBinder>::make();
+    hideLayer(3);
+    mLifecycleManager.applyTransactions(transactions);
+
+    update(mSnapshotBuilder);
+
+    bool foundInputLayer = false;
+    mSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+        if (snapshot.uniqueSequence == 3) {
+            EXPECT_TRUE(
+                    snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE));
+            EXPECT_FALSE(snapshot.isVisible);
+            foundInputLayer = true;
+        }
+    });
+    EXPECT_FALSE(foundInputLayer);
+}
+
 TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) {
     std::vector<uint32_t> visitedUniqueSequences;
     mSnapshotBuilder.forEachSnapshot(
@@ -1922,17 +1957,108 @@
 }
 
 TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) {
-    // By default the layer has no buffer, so we don't expect it to have an input info
+    // If a layer has no buffer or no color, it doesn't have an input info
+    setColor(111, {-1._hf, -1._hf, -1._hf});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 12, 121, 122, 1221, 13, 2});
     EXPECT_FALSE(getSnapshot(111)->hasInputInfo());
 
     setBuffer(111);
-
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
 
     EXPECT_TRUE(getSnapshot(111)->hasInputInfo());
     EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
             gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL));
-    EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
+}
+
+// content dirty test
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentAlphaChanges) {
+    setAlpha(1, 0.5);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+    EXPECT_TRUE(getSnapshot(11)->contentDirty);
+    EXPECT_TRUE(getSnapshot(111)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+    EXPECT_FALSE(getSnapshot(11)->contentDirty);
+    EXPECT_FALSE(getSnapshot(111)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenAutoRefresh) {
+    setAutoRefresh(1, true);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates don't clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // second update after removing auto refresh will clear content dirty
+    setAutoRefresh(1, false);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenColorChanges) {
+    setColor(1, {1, 2, 3});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+
+TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) {
+    setPosition(1, 2, 3);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->contentDirty);
+
+    // subsequent updates clear the dirty bit
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_FALSE(getSnapshot(1)->contentDirty);
+}
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Flag disabled, skipping test";
+    }
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().layerId = 1;
+    transactions.back().states.front().state.layerId = 1;
+    transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged;
+    transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3);
+
+    mLifecycleManager.applyTransactions(transactions);
+    EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+    update(mSnapshotBuilder);
+
+    EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged);
+    EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3));
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) {
+    if (!com_android_graphics_libgui_flags_apply_picture_profiles()) {
+        GTEST_SKIP() << "Flag disabled, skipping test";
+    }
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().layerId = 1;
+    transactions.back().states.front().state.layerId = 1;
+    transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged;
+    transactions.back().states.front().state.appContentPriority = 3;
+
+    mLifecycleManager.applyTransactions(transactions);
+    EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content);
+
+    update(mSnapshotBuilder);
+
+    EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3);
 }
 
 } // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 71f9f88..908637a 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -159,9 +159,9 @@
     constexpr VsyncId vsyncId{42};
 
     EXPECT_CALL(mTokenManager,
-                generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(),
-                                                                        kEndTime.ns(),
-                                                                        kPresentTime.ns())))
+                generateTokenForPredictions(
+                        frametimeline::TimelineItem(kStartTime.ns(), kEndTime.ns(),
+                                                    kPresentTime.ns(), kPresentTime.ns())))
             .WillOnce(Return(ftl::to_underlying(vsyncId)));
     EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1);
     EXPECT_NO_FATAL_FAILURE(
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index c879280..5c25f34 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -17,7 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisorTest"
 
-#include <DisplayHardware/PowerAdvisor.h>
+#include "PowerAdvisor/PowerAdvisor.h"
+
 #include <android_os.h>
 #include <binder/Status.h>
 #include <com_android_graphics_surfaceflinger_flags.h>
@@ -29,18 +30,17 @@
 #include <ui/DisplayId.h>
 #include <chrono>
 #include <future>
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockPowerHalController.h"
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "mock/PowerAdvisor/MockPowerHalController.h"
+#include "mock/PowerAdvisor/MockPowerHintSessionWrapper.h"
 
 using namespace android;
-using namespace android::Hwc2::mock;
+using namespace android::adpf::mock;
 using namespace android::hardware::power;
 using namespace std::chrono_literals;
 using namespace testing;
 using namespace android::power;
 
-namespace android::Hwc2::impl {
+namespace android::adpf::impl {
 
 class PowerAdvisorTest : public testing::Test {
 public:
@@ -73,7 +73,6 @@
     void testGpuScenario(GpuTestConfig& config, WorkDuration& ret);
 
 protected:
-    TestableSurfaceFlinger mFlinger;
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
     MockPowerHalController* mMockPowerHalController;
     std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
@@ -85,6 +84,7 @@
     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);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, false);
 };
 
 bool PowerAdvisorTest::sessionExists() {
@@ -97,7 +97,7 @@
 }
 
 void PowerAdvisorTest::SetUp() {
-    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
+    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>([]() {}, 80ms);
     mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
     mMockPowerHalController =
             reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
@@ -184,6 +184,7 @@
     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);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, config.usesFmq);
     mPowerAdvisor->onBootFinished();
     bool expectsFmqSuccess = config.usesSharedFmqFlag && !config.fmqFull;
     if (config.usesFmq) {
@@ -789,6 +790,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(true, false);
     auto startTime = uptimeNanos();
@@ -807,6 +809,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint_noSharedFlag) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(false, false);
     SessionHint hint;
@@ -821,6 +824,7 @@
 
 TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) {
     SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_fmq_sf, true);
     mPowerAdvisor->onBootFinished();
     SetUpFmq(true, true);
     ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
@@ -839,4 +843,4 @@
 }
 
 } // namespace
-} // namespace android::Hwc2::impl
+} // namespace android::adpf::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 9efe73d..9b3cba5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -100,7 +100,9 @@
     const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
 
     using RefreshRateSelector::GetRankedFrameRatesCache;
-    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+    auto& mutableGetRankedRefreshRatesCache() NO_THREAD_SAFETY_ANALYSIS {
+        return mGetRankedFrameRatesCache;
+    }
 
     auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
                              GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
@@ -138,7 +140,9 @@
         return setPolicy(policy);
     }
 
-    const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+    const auto& getPrimaryFrameRates() const NO_THREAD_SAFETY_ANALYSIS {
+        return mPrimaryFrameRates;
+    }
 };
 
 class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
@@ -2108,6 +2112,46 @@
     EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
 }
 
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    // Device with VRR config mode
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::Normal;
+    lr1.name = "ExplicitCategory Normal";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+    // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+    // However see 60 due to Normal vote.
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz,
+                           actualRankedFrameRates.ranking.front().frameRateMode);
+    EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::HighHint;
+    lr1.name = "ExplicitCategory HighHint";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+    actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+    EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz,
+                           actualRankedFrameRates.ranking.front().frameRateMode);
+    EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+}
+
 TEST_P(RefreshRateSelectorTest,
        getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) {
     SET_FLAG_FOR_TEST(flags::vrr_config, false);
@@ -3693,6 +3737,51 @@
     EXPECT_TRUE(frameRateOverrides.empty());
 }
 
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    // Device with VRR config mode
+    auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::Normal;
+    lr1.name = "ExplicitCategory Normal";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // No global touch boost, for example a game that uses setFrameRate(30, default compatibility).
+    // The `displayFrameRate` is 60.
+    // However 30 Default app still gets frame rate override.
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {});
+    EXPECT_EQ(2u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+    ASSERT_EQ(1u, frameRateOverrides.count(5678));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+
+    lr1.vote = LayerVoteType::ExplicitCategory;
+    lr1.frameRateCategory = FrameRateCategory::HighHint;
+    lr1.name = "ExplicitCategory HighHint";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.name = "30Hz ExplicitDefault";
+    // Gets touch boost because the touched (HighHint) app is different from the 30 Default app.
+    // The `displayFrameRate` is 120 (late touch boost).
+    // However 30 Default app still gets frame rate override.
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(5678));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(5678));
+}
+
 TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) {
     if (GetParam() == Config::FrameRateOverride::Disabled) {
         return;
@@ -4578,5 +4667,47 @@
         EXPECT_EQ(120_Hz, primaryRefreshRates[i].modePtr->getPeakFps());
     }
 }
+
+TEST_P(RefreshRateSelectorTest, getSupportedFrameRates) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_60_90, kModeId90);
+    const FpsRange range60 = {0_Hz, 60_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {kModeId60, {range60, range60}, {range60, range60}}));
+
+    // Irrespective of the policy we get the full range of possible frame rates
+    const std::vector<float> expected = {90.0f, 60.0f, 45.0f, 30.0f, 22.5f, 20.0f};
+
+    const auto allSupportedFrameRates = selector.getSupportedFrameRates();
+    ASSERT_EQ(expected.size(), allSupportedFrameRates.size());
+    for (size_t i = 0; i < expected.size(); i++) {
+        EXPECT_EQ(expected[i], allSupportedFrameRates[i])
+                << "expected " << expected[i] << " received " << allSupportedFrameRates[i];
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getSupportedFrameRatesArr) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    const auto selector = createSelector(kVrrMode_120, kModeId120);
+
+    const std::vector<float> expected = {120.0f, 80.0f,   60.0f, 48.0f,   40.0f, 34.285f,
+                                         30.0f,  26.666f, 24.0f, 21.818f, 20.0f};
+
+    const auto allSupportedFrameRates = selector.getSupportedFrameRates();
+    ASSERT_EQ(expected.size(), allSupportedFrameRates.size());
+    constexpr float kEpsilon = 0.001f;
+    for (size_t i = 0; i < expected.size(); i++) {
+        EXPECT_TRUE(std::abs(expected[i] - allSupportedFrameRates[i]) <= kEpsilon)
+                << "expected " << expected[i] << " received " << allSupportedFrameRates[i];
+    }
+}
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index ac09cbc..1fc874d 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -222,7 +222,7 @@
     const auto selectorPtr =
             std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
     mScheduler->registerDisplay(kDisplayId1, selectorPtr);
-    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
 
     mScheduler->setContentRequirements({kLayer});
 
@@ -250,7 +250,7 @@
     EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
 
     mScheduler->touchTimerCallback(TimerState::Reset);
-    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+    mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true);
 }
 
 TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 8699621..b0dd5c2 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -34,7 +34,7 @@
                                                static_cast<hal::HWConfigId>(        \
                                                        ftl::to_underlying(modeId)), \
                                                _, _))                               \
-            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)))
+            .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::Error::NONE)))
 
 namespace android {
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 20a3315..6cc6322 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -18,12 +18,13 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include <gui/SurfaceComposerClient.h>
+#include "DisplayHardware/Hal.h"
 #include "DisplayTransactionTestHelpers.h"
 
 namespace android {
 
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::hal::Error;
 
 class NotifyExpectedPresentTest : public DisplayTransactionTest {
 public:
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index c3934e6..3ae5ed9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -21,6 +21,8 @@
 
 #include "CommitAndCompositeTest.h"
 
+#include "DisplayHardware/Hal.h"
+
 using namespace std::chrono_literals;
 using testing::_;
 using testing::Return;
@@ -40,7 +42,7 @@
     EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
         constexpr Duration kMockHwcRunTime = 20ms;
         std::this_thread::sleep_for(kMockHwcRunTime);
-        return hardware::graphics::composer::V2_1::Error::NONE;
+        return hardware::graphics::composer::hal::Error::NONE;
     });
     EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(1);
 
@@ -61,7 +63,7 @@
     EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _, _)).WillOnce([] {
         constexpr Duration kMockHwcRunTime = 20ms;
         std::this_thread::sleep_for(kMockHwcRunTime);
-        return hardware::graphics::composer::V2_1::Error::NONE;
+        return hardware::graphics::composer::hal::Error::NONE;
     });
     EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(0);
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c043b88..7f0b7a6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -44,22 +44,22 @@
 #include "NativeWindowSurface.h"
 #include "RenderArea.h"
 #include "Scheduler/RefreshRateSelector.h"
+#include "Scheduler/VSyncTracker.h"
+#include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
 #include "TestableScheduler.h"
 #include "android/gui/ISurfaceComposerClient.h"
+
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
 #include "mock/MockFrameTracer.h"
 #include "mock/MockSchedulerCallback.h"
-#include "mock/system/window/MockNativeWindow.h"
-
-#include "Scheduler/VSyncTracker.h"
-#include "Scheduler/VsyncController.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
+#include "mock/PowerAdvisor/MockPowerAdvisor.h"
+#include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 
@@ -190,7 +190,7 @@
                 &mFlinger->mCompositionEngine->getHwComposer());
     }
 
-    void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
+    void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) {
         mFlinger->mPowerAdvisor = std::move(powerAdvisor);
     }
 
@@ -472,12 +472,10 @@
         ScreenCaptureResults captureResults;
         auto displayState = std::optional{display->getCompositionDisplay()->getState()};
         auto layers = getLayerSnapshotsFn();
-        auto layerFEs = mFlinger->extractLayerFEs(layers);
 
         return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
                                           false /* grayscale */, false /* isProtected */,
-                                          false /* attachGainmap */, captureResults, displayState,
-                                          layers, layerFEs);
+                                          captureResults, displayState, layers);
     }
 
     auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
@@ -637,7 +635,7 @@
     void destroyAllLayerHandles() {
         ftl::FakeGuard guard(kMainThreadContext);
         for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
-            mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+            mFlinger->onHandleDestroyed(legacyLayer, layerId);
         }
     }
 
@@ -1162,7 +1160,7 @@
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
     std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
-    Hwc2::mock::PowerAdvisor mPowerAdvisor;
+    adpf::mock::PowerAdvisor mPowerAdvisor;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index fab1f6d..1e8cd0a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -387,7 +387,7 @@
 
         state.state.what = what;
         if (what & layer_state_t::eCropChanged) {
-            state.state.crop = Rect(1, 2, 3, 4);
+            state.state.crop = FloatRect(1, 2, 3, 4);
         }
         if (what & layer_state_t::eFlagsChanged) {
             state.state.flags = layer_state_t::eEnableBackpressure;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f472d8f..0d5266e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -20,6 +20,7 @@
 
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/Hal.h"
 
 namespace android {
 
@@ -34,9 +35,9 @@
 using android::hardware::graphics::common::V1_2::Dataspace;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
+using android::hardware::graphics::composer::hal::Error;
 using android::hardware::graphics::composer::V2_1::Config;
 using android::hardware::graphics::composer::V2_1::Display;
-using android::hardware::graphics::composer::V2_1::Error;
 using android::hardware::graphics::composer::V2_1::IComposer;
 using android::hardware::graphics::composer::V2_1::Layer;
 using android::hardware::graphics::composer::V2_4::IComposerCallback;
@@ -142,8 +143,8 @@
                  V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
     MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
     MOCK_METHOD4(setActiveConfigWithConstraints,
-                 V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
-                             VsyncPeriodChangeTimeline*));
+                 Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+                       VsyncPeriodChangeTimeline*));
     MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
     MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
     MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
@@ -182,10 +183,13 @@
     MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
     MOCK_METHOD(
             Error, getRequestedLuts,
-            (Display,
+            (Display, std::vector<Layer>*,
              std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*));
     MOCK_METHOD(Error, setLayerLuts,
-                (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&));
+                (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&));
+    MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*));
+    MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id));
+    MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 5edd2cd..ec065a7 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <gmock/gmock.h>
+#include <cstdint>
 
 #include "DisplayHardware/HWC2.h"
 
@@ -52,7 +53,7 @@
                 (override));
     MOCK_METHOD(hal::Error, getName, (std::string *), (const, override));
     MOCK_METHOD(hal::Error, getRequests,
-                (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)),
+                (hal::DisplayRequest*, (std::unordered_map<Layer*, hal::LayerRequest>*)),
                 (override));
     MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (),
                 (const, override));
@@ -85,8 +86,8 @@
     MOCK_METHOD(ftl::Future<hal::Error>, setDisplayBrightness,
                 (float, float, const Hwc2::Composer::DisplayBrightnessOptions &), (override));
     MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
-                (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
-                 hal::VsyncPeriodChangeTimeline *),
+                (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+                 hal::VsyncPeriodChangeTimeline*),
                 (override));
     MOCK_METHOD(hal::Error, setBootDisplayConfig, (hal::HWConfigId), (override));
     MOCK_METHOD(hal::Error, clearBootDisplayConfig, (), (override));
@@ -111,7 +112,9 @@
                 (aidl::android::hardware::graphics::composer3::OverlayProperties *),
                 (const override));
     MOCK_METHOD(hal::Error, getRequestedLuts,
-                (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
+                (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override));
+    MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override));
+    MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
                 (override));
 };
 
@@ -126,6 +129,8 @@
                 (uint32_t, const android::sp<android::GraphicBuffer> &,
                  const android::sp<android::Fence> &),
                 (override));
+    MOCK_METHOD(hal::Error, setBufferSlotsToClear,
+                (const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot), (override));
     MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
     MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
     MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color),
@@ -147,8 +152,10 @@
                 (const std::string &, bool, const std::vector<uint8_t> &), (override));
     MOCK_METHOD(hal::Error, setBrightness, (float), (override));
     MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override));
-    MOCK_METHOD(hal::Error, setLuts,
-                (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override));
+    MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&),
+                (override));
+    MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&),
+                (override));
 };
 
 } // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
similarity index 81%
rename from services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
index ae52670..f310633 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp
@@ -16,17 +16,11 @@
 
 #include "MockHWComposer.h"
 
-namespace android {
-
-// This will go away once HWComposer is moved into the "backend" library
-HWComposer::~HWComposer() = default;
-
-namespace mock {
+namespace android::mock {
 
 // The Google Mock documentation recommends explicit non-header instantiations
 // for better compile time performance.
 HWComposer::HWComposer() = default;
 HWComposer::~HWComposer() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
new file mode 100644
index 0000000..88f83d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/HWComposer.h"
+
+namespace android::mock {
+
+class HWComposer : public android::HWComposer {
+public:
+    using HWDisplayId = android::hardware::graphics::composer::hal::HWDisplayId;
+    using PowerMode = android::hardware::graphics::composer::hal::PowerMode;
+
+    HWComposer();
+    ~HWComposer() override;
+
+    MOCK_METHOD(void, setCallback, (HWC2::ComposerCallback&), (override));
+    MOCK_METHOD(bool, getDisplayIdentificationData,
+                (HWDisplayId, uint8_t*, DisplayIdentificationData*), (const, override));
+    MOCK_METHOD(bool, hasCapability, (aidl::android::hardware::graphics::composer3::Capability),
+                (const, override));
+    MOCK_METHOD(bool, hasDisplayCapability,
+                (HalDisplayId, aidl::android::hardware::graphics::composer3::DisplayCapability),
+                (const, override));
+
+    MOCK_METHOD(size_t, getMaxVirtualDisplayCount, (), (const, override));
+    MOCK_METHOD(size_t, getMaxVirtualDisplayDimension, (), (const, override));
+    MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*),
+                (override));
+    MOCK_METHOD(void, allocatePhysicalDisplay,
+                (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override));
+
+    MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override));
+    MOCK_METHOD(status_t, getDeviceCompositionChanges,
+                (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t,
+                 Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*));
+    MOCK_METHOD(status_t, setClientTarget,
+                (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace,
+                 float),
+                (override));
+    MOCK_METHOD(status_t, presentAndGetReleaseFences,
+                (HalDisplayId, std::optional<std::chrono::steady_clock::time_point>), (override));
+    MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
+    MOCK_METHOD(status_t, setPowerMode, (PhysicalDisplayId, PowerMode), (override));
+    MOCK_METHOD(status_t, setColorTransform, (HalDisplayId, const mat4&), (override));
+    MOCK_METHOD(void, disconnectDisplay, (HalDisplayId), (override));
+    MOCK_METHOD(sp<Fence>, getPresentFence, (HalDisplayId), (const, override));
+    MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(sp<Fence>, getLayerReleaseFence, (HalDisplayId, HWC2::Layer*), (const, override));
+    MOCK_METHOD(status_t, setOutputBuffer,
+                (HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&), (override));
+    MOCK_METHOD(void, clearReleaseFences, (HalDisplayId), (override));
+    MOCK_METHOD(status_t, getHdrCapabilities, (HalDisplayId, HdrCapabilities*), (override));
+    MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (HalDisplayId), (const, override));
+    MOCK_METHOD(std::vector<ui::RenderIntent>, getRenderIntents, (HalDisplayId, ui::ColorMode),
+                (const, override));
+    MOCK_METHOD(mat4, getDataspaceSaturationMatrix, (HalDisplayId, ui::Dataspace), (override));
+    MOCK_METHOD(status_t, getDisplayedContentSamplingAttributes,
+                (HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*), (override));
+    MOCK_METHOD(status_t, setDisplayContentSamplingEnabled, (HalDisplayId, bool, uint8_t, uint64_t),
+                (override));
+    MOCK_METHOD(status_t, getDisplayedContentSample,
+                (HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*), (override));
+    MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness,
+                (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&),
+                (override));
+    MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug,
+                (hal::HWDisplayId, hal::Connection), (override));
+    MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override));
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t));
+    MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override));
+    MOCK_METHOD(bool, isConnected, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(std::vector<HWComposer::HWCDisplayMode>, getModes, (PhysicalDisplayId, int32_t),
+                (const, override));
+    MOCK_METHOD((ftl::Expected<hal::HWConfigId, status_t>), getActiveMode, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(std::vector<ui::ColorMode>, getColorModes, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(status_t, setActiveColorMode, (PhysicalDisplayId, ui::ColorMode, ui::RenderIntent),
+                (override));
+    MOCK_METHOD(ui::DisplayConnectionType, getDisplayConnectionType, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD((ftl::Expected<nsecs_t, status_t>), getDisplayVsyncPeriod, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(status_t, setActiveModeWithConstraints,
+                (PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&,
+                 hal::VsyncPeriodChangeTimeline*),
+                (override));
+    MOCK_METHOD(status_t, setBootDisplayMode, (PhysicalDisplayId, hal::HWConfigId), (override));
+    MOCK_METHOD(status_t, clearBootDisplayMode, (PhysicalDisplayId), (override));
+    MOCK_METHOD(std::optional<hal::HWConfigId>, getPreferredBootDisplayMode, (PhysicalDisplayId),
+                (override));
+
+    MOCK_METHOD(std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>,
+                getHdrConversionCapabilities, (), (const, override));
+    MOCK_METHOD(status_t, setHdrConversionStrategy,
+                (aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                 aidl::android::hardware::graphics::common::Hdr*),
+                (override));
+    MOCK_METHOD(status_t, setAutoLowLatencyMode, (PhysicalDisplayId, bool), (override));
+    MOCK_METHOD(status_t, getSupportedContentTypes,
+                (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override));
+    MOCK_METHOD(status_t, setContentType, (PhysicalDisplayId, hal::ContentType)), (override);
+    MOCK_METHOD((const std::unordered_map<std::string, bool>&), getSupportedLayerGenericMetadata,
+                (), (const, override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
+    MOCK_METHOD(void, dumpOverlayProperties, (std::string&), (const, override));
+    MOCK_METHOD(android::Hwc2::Composer*, getComposer, (), (const, override));
+
+    MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+    MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+    MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+    MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+                (const, override));
+    MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(status_t, getDisplayDecorationSupport,
+                (PhysicalDisplayId,
+                 std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
+                         support),
+                (override));
+    MOCK_METHOD(status_t, setIdleTimerEnabled, (PhysicalDisplayId, std::chrono::milliseconds),
+                (override));
+    MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override));
+    MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId),
+                (const, override));
+    MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override));
+    MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&,
+                getOverlaySupport, (), (const, override));
+    MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
+    MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
+    MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (),
+                (override));
+    MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId));
+    MOCK_METHOD(status_t, setDisplayPictureProfileHandle,
+                (PhysicalDisplayId, const PictureProfileHandle&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
deleted file mode 100644
index 2323ebb..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/DisplayHardware/MockIPower.h"
-
-namespace android::Hwc2::mock {
-
-// Explicit default instantiation is recommended.
-MockIPower::MockIPower() = default;
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
deleted file mode 100644
index ed1405b..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "binder/Status.h"
-
-// FMQ library in IPower does questionable conversions
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
-#pragma clang diagnostic pop
-
-#include <gmock/gmock.h>
-
-using aidl::android::hardware::power::Boost;
-using aidl::android::hardware::power::ChannelConfig;
-using aidl::android::hardware::power::IPower;
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionTag;
-
-using aidl::android::hardware::power::Mode;
-using android::binder::Status;
-
-namespace android::Hwc2::mock {
-
-class MockIPower : public IPower {
-public:
-    MockIPower();
-
-    MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, createHintSession,
-                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session),
-                (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig,
-                (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
-                 int64_t durationNanos, SessionTag tag, SessionConfig* config,
-                 std::shared_ptr<IPowerHintSession>* _aidl_return),
-                (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel,
-                (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
-    MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
-    MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
-    MOCK_METHOD(bool, isRemote, (), (override));
-};
-
-} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 7398cbe..3036fec 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -30,9 +30,12 @@
     MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags),
                 (const, override));
     MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
+    MOCK_METHOD(void, omitVsyncDispatching, (bool), (override));
     MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
     MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
     MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+    MOCK_METHOD(void, onModeRejected, (PhysicalDisplayId displayId, DisplayModeId modeId),
+                (override));
     MOCK_METHOD(void, onFrameRateOverridesChanged,
                 (PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
     MOCK_METHOD(void, dump, (std::string&), (const, override));
@@ -52,6 +55,7 @@
     MOCK_METHOD(void, onHdcpLevelsChanged,
                 (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel),
                 (override));
+    MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 45f86fa..59f0966 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -19,6 +19,8 @@
 #include <gmock/gmock.h>
 #include <optional>
 
+#include "Layer.h"
+
 namespace android::mock {
 
 class MockLayer : public Layer {
@@ -26,8 +28,11 @@
     MockLayer(SurfaceFlinger* flinger, std::string name)
           : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
 
-    MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
+    MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> id)
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, id)) {}
+
+    MockLayer(SurfaceFlinger* flinger, std::optional<uint32_t> id)
+          : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 0, {}, id)) {}
 
     explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index d45cc66..25dd68e 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -29,7 +29,7 @@
     MOCK_METHOD(void, onChoreographerAttached, (), (override));
     MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
                 (override));
-    MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+    MOCK_METHOD(void, onCommitNotComposited, (), (override));
     MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
 };
 
@@ -39,7 +39,7 @@
     void kernelTimerChanged(bool) override {}
     void onChoreographerAttached() override {}
     void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
-    void onCommitNotComposited(PhysicalDisplayId) override {}
+    void onCommitNotComposited() override {}
     void vrrDisplayIdle(bool) override {}
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
index 1ba38a8..f4c1e52 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp
@@ -16,10 +16,10 @@
 
 #include "MockPowerAdvisor.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 // Explicit default instantiation is recommended.
 PowerAdvisor::PowerAdvisor() = default;
 PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
index 4efdfe8..5c4512a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h
@@ -18,11 +18,11 @@
 
 #include <gmock/gmock.h>
 
-#include "DisplayHardware/PowerAdvisor.h"
+#include "PowerAdvisor/PowerAdvisor.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
-class PowerAdvisor : public android::Hwc2::PowerAdvisor {
+class PowerAdvisor : public android::adpf::PowerAdvisor {
 public:
     PowerAdvisor();
     ~PowerAdvisor() override;
@@ -65,4 +65,4 @@
     MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
similarity index 91%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
index 3ec5c2d..3b8de55 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp
@@ -16,9 +16,9 @@
 
 #include "MockPowerHalController.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 MockPowerHalController::MockPowerHalController() = default;
 MockPowerHalController::~MockPowerHalController() = default;
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
similarity index 92%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
index af1862d..891f507 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h
@@ -19,10 +19,7 @@
 #include <gmock/gmock.h>
 #include <scheduler/Time.h>
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
 #include <powermanager/PowerHalController.h>
-#pragma clang diagnostic pop
 
 namespace android {
 namespace hardware {
@@ -32,7 +29,7 @@
 } // namespace hardware
 } // namespace android
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 using android::power::HalResult;
 
@@ -57,6 +54,8 @@
     MOCK_METHOD(HalResult<aidl::android::hardware::power::ChannelConfig>, getSessionChannel,
                 (int tgid, int uid), (override));
     MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override));
+    MOCK_METHOD(HalResult<aidl::android::hardware::power::SupportInfo>, getSupportInfo, (),
+                (override));
 };
 
-} // namespace android::Hwc2::mock
\ No newline at end of file
+} // namespace android::adpf::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
similarity index 85%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
index d383283..cb39783 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
+#include "MockPowerHintSessionWrapper.h"
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 // Explicit default instantiation is recommended.
 MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
       : power::PowerHintSessionWrapper(nullptr) {}
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
similarity index 88%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
rename to services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
index bc6950c..4518de8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
+++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h
@@ -16,13 +16,7 @@
 
 #pragma once
 
-#include "binder/Status.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
 #include <powermanager/PowerHintSessionWrapper.h>
-#pragma clang diagnostic pop
 
 #include <gmock/gmock.h>
 
@@ -34,7 +28,7 @@
 
 using namespace aidl::android::hardware::power;
 
-namespace android::Hwc2::mock {
+namespace android::adpf::mock {
 
 class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
 public:
@@ -52,4 +46,4 @@
     MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override));
 };
 
-} // namespace android::Hwc2::mock
+} // namespace android::adpf::mock