Merge Android U (ab/10368041)

Bug: 291102124
Merged-In: I9b175092d433fc6d7b22b437a09d76d3d2e0ce14
Change-Id: If8a2897a99b111ba107f33c19537d40bcbdc802e
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cbb95f9..5683a92 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -18,14 +18,16 @@
         "-Wunused",
         "-Wunreachable-code",
         "-Wconversion",
+        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_defaults",
     defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
+        "librenderengine_deps",
         "surfaceflinger_defaults",
-        "skia_renderengine_deps",
     ],
     cflags: [
         "-DLOG_TAG=\"SurfaceFlinger\"",
@@ -45,10 +47,7 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer3-V1-ndk",
-        "android.hardware.power@1.0",
-        "android.hardware.power@1.3",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V4-cpp",
         "libbase",
         "libbinder",
         "libbinder_ndk",
@@ -62,6 +61,7 @@
         "liblayers_proto",
         "liblog",
         "libnativewindow",
+        "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
@@ -83,7 +83,6 @@
         "libserviceutils",
         "libshaders",
         "libtonemap",
-        "libtrace_proto",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
@@ -105,8 +104,7 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer3-V1-ndk",
-        "android.hardware.power@1.3",
+        "libpowermanager",
         "libhidlbase",
         "libtimestats",
     ],
@@ -141,26 +139,29 @@
     name: "libsurfaceflinger_sources",
     srcs: [
         "BackgroundExecutor.cpp",
-        "BufferLayer.cpp",
-        "BufferLayerConsumer.cpp",
-        "BufferQueueLayer.cpp",
-        "BufferStateLayer.cpp",
-        "ClientCache.cpp",
         "Client.cpp",
-        "EffectLayer.cpp",
-        "ContainerLayer.cpp",
+        "ClientCache.cpp",
+        "Display/DisplaySnapshot.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/AidlComposerHal.cpp",
-        "DisplayHardware/HidlComposerHal.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",
+        "FrontEnd/LayerSnapshotBuilder.cpp",
+        "FrontEnd/LayerHierarchy.cpp",
+        "FrontEnd/LayerLifecycleManager.cpp",
+        "FrontEnd/RequestedLayerState.cpp",
+        "FrontEnd/TransactionHandler.cpp",
         "FlagManager.cpp",
         "FpsReporter.cpp",
         "FrameTracer/FrameTracer.cpp",
@@ -168,23 +169,21 @@
         "HdrLayerInfoReporter.cpp",
         "WindowInfosListenerInvoker.cpp",
         "Layer.cpp",
+        "LayerFE.cpp",
         "LayerProtoHelper.cpp",
-        "LayerRejecter.cpp",
         "LayerRenderArea.cpp",
         "LayerVector.cpp",
-        "MonitoredProducer.cpp",
         "NativeWindowSurface.cpp",
         "RefreshRateOverlay.cpp",
         "RegionSamplingThread.cpp",
         "RenderArea.cpp",
-        "Scheduler/DispSyncSource.cpp",
         "Scheduler/EventThread.cpp",
         "Scheduler/FrameRateOverrideMappings.cpp",
         "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
         "Scheduler/LayerInfo.cpp",
         "Scheduler/MessageQueue.cpp",
-        "Scheduler/RefreshRateConfigs.cpp",
+        "Scheduler/RefreshRateSelector.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
@@ -192,10 +191,10 @@
         "Scheduler/VsyncConfiguration.cpp",
         "Scheduler/VsyncModulator.cpp",
         "Scheduler/VsyncSchedule.cpp",
+        "ScreenCaptureOutput.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
-        "SurfaceInterceptor.cpp",
         "Tracing/LayerTracing.cpp",
         "Tracing/TransactionTracing.cpp",
         "Tracing/TransactionProtoParser.cpp",
@@ -230,7 +229,6 @@
     ],
     static_libs: [
         "libserviceutils",
-        "libtrace_proto",
     ],
 }
 
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
index a15de2b..6ddf790 100644
--- a/services/surfaceflinger/BackgroundExecutor.cpp
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -28,29 +28,19 @@
 ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor);
 
 BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
+    // mSemaphore must be initialized before any calls to
+    // BackgroundExecutor::sendCallbacks. For this reason, we initialize it
+    // within the constructor instead of within mThread.
+    LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
     mThread = std::thread([&]() {
-        LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
         while (!mDone) {
             LOG_ALWAYS_FATAL_IF(sem_wait(&mSemaphore), "sem_wait failed (%d)", errno);
-
-            ftl::SmallVector<Work*, 10> workItems;
-
-            Work* work = mWorks.pop();
-            while (work) {
-                workItems.push_back(work);
-                work = mWorks.pop();
+            auto callbacks = mCallbacksQueue.pop();
+            if (!callbacks) {
+                continue;
             }
-
-            // Sequence numbers are guaranteed to be in intended order, as we assume a single
-            // producer and single consumer.
-            std::stable_sort(workItems.begin(), workItems.end(), [](Work* left, Work* right) {
-                return left->sequence < right->sequence;
-            });
-            for (Work* work : workItems) {
-                for (auto& task : work->tasks) {
-                    task();
-                }
-                delete work;
+            for (auto& callback : *callbacks) {
+                callback();
             }
         }
     });
@@ -66,12 +56,8 @@
 }
 
 void BackgroundExecutor::sendCallbacks(Callbacks&& tasks) {
-    Work* work = new Work();
-    work->sequence = mSequence;
-    work->tasks = std::move(tasks);
-    mWorks.push(work);
-    mSequence++;
+    mCallbacksQueue.push(std::move(tasks));
     LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
index eeaf3bd..0fae5a5 100644
--- a/services/surfaceflinger/BackgroundExecutor.h
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -16,15 +16,13 @@
 
 #pragma once
 
-#include <Tracing/LocklessStack.h>
-#include <android-base/thread_annotations.h>
 #include <ftl/small_vector.h>
 #include <semaphore.h>
 #include <utils/Singleton.h>
-#include <mutex>
-#include <queue>
 #include <thread>
 
+#include "LocklessQueue.h"
+
 namespace android {
 
 // Executes tasks off the main thread.
@@ -34,24 +32,14 @@
     ~BackgroundExecutor();
     using Callbacks = ftl::SmallVector<std::function<void()>, 10>;
     // Queues callbacks onto a work queue to be executed by a background thread.
-    // Note that this is not thread-safe - a single producer is assumed.
+    // This is safe to call from multiple threads.
     void sendCallbacks(Callbacks&& tasks);
 
 private:
     sem_t mSemaphore;
     std::atomic_bool mDone = false;
 
-    // Sequence number for work items.
-    // Work items are batched by sequence number. Work items for earlier sequence numbers are
-    // executed first. Work items with the same sequence number are executed in the same order they
-    // were added to the stack (meaning the stack must reverse the order after popping from the
-    // queue)
-    int32_t mSequence = 0;
-    struct Work {
-        int32_t sequence = 0;
-        Callbacks tasks;
-    };
-    LocklessStack<Work> mWorks;
+    LocklessQueue<Callbacks> mCallbacksQueue;
     std::thread mThread;
 };
 
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
deleted file mode 100644
index d9c89cd..0000000
--- a/services/surfaceflinger/BufferLayer.cpp
+++ /dev/null
@@ -1,814 +0,0 @@
-/*
- * Copyright (C) 2017 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"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "BufferLayer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BufferLayer.h"
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <cutils/compiler.h>
-#include <cutils/native_handle.h>
-#include <cutils/properties.h>
-#include <gui/BufferItem.h>
-#include <gui/BufferQueue.h>
-#include <gui/GLConsumer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/Surface.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/DebugUtils.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/NativeHandle.h>
-#include <utils/StopWatch.h>
-#include <utils/Trace.h>
-
-#include <cmath>
-#include <cstdlib>
-#include <mutex>
-#include <sstream>
-
-#include "Colorizer.h"
-#include "DisplayDevice.h"
-#include "FrameTracer/FrameTracer.h"
-#include "LayerRejecter.h"
-#include "TimeStats/TimeStats.h"
-
-namespace android {
-
-using gui::WindowInfo;
-
-static constexpr float defaultMaxLuminance = 1000.0;
-
-BufferLayer::BufferLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mTextureName(args.textureName),
-        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {
-    ALOGV("Creating Layer %s", getDebugName());
-
-    mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
-
-    mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
-    mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
-}
-
-BufferLayer::~BufferLayer() {
-    if (!isClone()) {
-        // The original layer and the clone layer share the same texture. Therefore, only one of
-        // the layers, in this case the original layer, needs to handle the deletion. The original
-        // layer and the clone should be removed at the same time so there shouldn't be any issue
-        // with the clone layer trying to use the deleted texture.
-        mFlinger->deleteTextureAsync(mTextureName);
-    }
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->onDestroy(layerId);
-    mFlinger->mFrameTracer->onDestroy(layerId);
-}
-
-void BufferLayer::useSurfaceDamage() {
-    if (mFlinger->mForceFullDamage) {
-        surfaceDamageRegion = Region::INVALID_REGION;
-    } else {
-        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
-    }
-}
-
-void BufferLayer::useEmptyDamage() {
-    surfaceDamageRegion.clear();
-}
-
-bool BufferLayer::isOpaque(const Layer::State& s) const {
-    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
-    // layer's opaque flag.
-    if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
-        return false;
-    }
-
-    // if the layer has the opaque flag, then we're always opaque,
-    // otherwise we use the current buffer's format.
-    return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
-}
-
-bool BufferLayer::canReceiveInput() const {
-    return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
-}
-
-bool BufferLayer::isVisible() const {
-    return !isHiddenByPolicy() && getAlpha() > 0.0f &&
-            (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
-}
-
-bool BufferLayer::isFixedSize() const {
-    return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-}
-
-bool BufferLayer::usesSourceCrop() const {
-    return true;
-}
-
-static constexpr mat4 inverseOrientation(uint32_t transform) {
-    const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
-    const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
-    const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
-    mat4 tr;
-
-    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        tr = tr * rot90;
-    }
-    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
-        tr = tr * flipH;
-    }
-    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
-        tr = tr * flipV;
-    }
-    return inverse(tr);
-}
-
-std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    ATRACE_CALL();
-
-    std::optional<compositionengine::LayerFE::LayerSettings> result =
-            Layer::prepareClientComposition(targetSettings);
-    if (!result) {
-        return result;
-    }
-
-    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
-        // For surfaceview of tv sideband, there is no activeBuffer
-        // in bufferqueue, we need return LayerSettings.
-        return result;
-    }
-    const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
-            ((isSecure() || isProtected()) && !targetSettings.isSecure);
-    const bool bufferCanBeUsedAsHwTexture =
-            mBufferInfo.mBuffer->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
-    compositionengine::LayerFE::LayerSettings& layer = *result;
-    if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
-        ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
-                 mName.c_str());
-        prepareClearClientComposition(layer, true /* blackout */);
-        return layer;
-    }
-
-    const State& s(getDrawingState());
-    layer.source.buffer.buffer = mBufferInfo.mBuffer;
-    layer.source.buffer.isOpaque = isOpaque(s);
-    layer.source.buffer.fence = mBufferInfo.mFence;
-    layer.source.buffer.textureName = mTextureName;
-    layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-    layer.source.buffer.isY410BT2020 = isHdrY410();
-    bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
-    bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
-    float maxLuminance = 0.f;
-    if (hasSmpte2086 && hasCta861_3) {
-        maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance,
-                                mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel);
-    } else if (hasSmpte2086) {
-        maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance;
-    } else if (hasCta861_3) {
-        maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel;
-    } else {
-        switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
-            case HAL_DATASPACE_TRANSFER_ST2084:
-            case HAL_DATASPACE_TRANSFER_HLG:
-                // Behavior-match previous releases for HDR content
-                maxLuminance = defaultMaxLuminance;
-                break;
-        }
-    }
-    layer.source.buffer.maxLuminanceNits = maxLuminance;
-    layer.frameNumber = mCurrentFrameNumber;
-    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
-
-    const bool useFiltering =
-            targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
-
-    // Query the texture matrix given our current filtering mode.
-    float textureMatrix[16];
-    getDrawingTransformMatrix(useFiltering, textureMatrix);
-
-    if (getTransformToDisplayInverse()) {
-        /*
-         * the code below applies the primary display's inverse transform to
-         * the texture transform
-         */
-        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        mat4 tr = inverseOrientation(transform);
-
-        /**
-         * TODO(b/36727915): This is basically a hack.
-         *
-         * Ensure that regardless of the parent transformation,
-         * this buffer is always transformed from native display
-         * orientation to display orientation. For example, in the case
-         * of a camera where the buffer remains in native orientation,
-         * we want the pixels to always be upright.
-         */
-        sp<Layer> p = mDrawingParent.promote();
-        if (p != nullptr) {
-            const auto parentTransform = p->getTransform();
-            tr = tr * inverseOrientation(parentTransform.getOrientation());
-        }
-
-        // and finally apply it to the original texture matrix
-        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
-        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
-    }
-
-    const Rect win{getBounds()};
-    float bufferWidth = getBufferSize(s).getWidth();
-    float bufferHeight = getBufferSize(s).getHeight();
-
-    // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
-    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
-    // ignore them.
-    if (!getBufferSize(s).isValid()) {
-        bufferWidth = float(win.right) - float(win.left);
-        bufferHeight = float(win.bottom) - float(win.top);
-    }
-
-    const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
-    const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
-    const float translateY = float(win.top) / bufferHeight;
-    const float translateX = float(win.left) / bufferWidth;
-
-    // Flip y-coordinates because GLConsumer expects OpenGL convention.
-    mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
-            mat4::translate(vec4(-.5, -.5, 0, 1)) *
-            mat4::translate(vec4(translateX, translateY, 0, 1)) *
-            mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
-
-    layer.source.buffer.useTextureFiltering = useFiltering;
-    layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
-
-    return layer;
-}
-
-bool BufferLayer::isHdrY410() const {
-    // pixel format is HDR Y410 masquerading as RGBA_1010102
-    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
-            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
-            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
-}
-
-sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
-    return asLayerFE();
-}
-
-compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() {
-    return mCompositionState.get();
-}
-
-const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const {
-    return mCompositionState.get();
-}
-
-void BufferLayer::preparePerFrameCompositionState() {
-    Layer::preparePerFrameCompositionState();
-
-    // Sideband layers
-    auto* compositionState = editCompositionState();
-    if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
-        compositionState->compositionType =
-                aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
-        return;
-    } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
-        compositionState->compositionType =
-                aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
-    } else {
-        // Normal buffer layers
-        compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
-        compositionState->compositionType = mPotentialCursor
-                ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
-                : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
-    }
-
-    compositionState->buffer = getBuffer();
-    compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
-            ? 0
-            : mBufferInfo.mBufferSlot;
-    compositionState->acquireFence = mBufferInfo.mFence;
-    compositionState->frameNumber = mBufferInfo.mFrameNumber;
-    compositionState->sidebandStreamHasFrame = false;
-}
-
-bool BufferLayer::onPreComposition(nsecs_t) {
-    return hasReadyFrame();
-}
-namespace {
-TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
-    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
-    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
-    const auto frameRateCompatibility = [frameRate] {
-        switch (frameRate.type) {
-            case Layer::FrameRateCompatibility::Default:
-                return FrameRateCompatibility::Default;
-            case Layer::FrameRateCompatibility::ExactOrMultiple:
-                return FrameRateCompatibility::ExactOrMultiple;
-            default:
-                return FrameRateCompatibility::Undefined;
-        }
-    }();
-
-    const auto seamlessness = [frameRate] {
-        switch (frameRate.seamlessness) {
-            case scheduler::Seamlessness::OnlySeamless:
-                return Seamlessness::ShouldBeSeamless;
-            case scheduler::Seamlessness::SeamedAndSeamless:
-                return Seamlessness::NotRequired;
-            default:
-                return Seamlessness::Undefined;
-        }
-    }();
-
-    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
-                                       .frameRateCompatibility = frameRateCompatibility,
-                                       .seamlessness = seamlessness};
-}
-} // namespace
-
-void BufferLayer::onPostComposition(const DisplayDevice* display,
-                                    const std::shared_ptr<FenceTime>& glDoneFence,
-                                    const std::shared_ptr<FenceTime>& presentFence,
-                                    const CompositorTiming& compositorTiming) {
-    // mFrameLatencyNeeded is true when a new frame was latched for the
-    // composition.
-    if (!mBufferInfo.mFrameLatencyNeeded) return;
-
-    // Update mFrameEventHistory.
-    finalizeFrameEventHistory(glDoneFence, compositorTiming);
-
-    // Update mFrameTracker.
-    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
-    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
-
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
-
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer && outputLayer->requiresClientComposition()) {
-        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
-        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                               clientCompositionTimestamp,
-                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
-        // Update the SurfaceFrames in the drawing state
-        if (mDrawingState.bufferSurfaceFrameTX) {
-            mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
-        }
-        for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
-            surfaceFrame->setGpuComposition();
-        }
-    }
-
-    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
-    if (frameReadyFence->isValid()) {
-        mFrameTracker.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);
-    }
-
-    if (display) {
-        const Fps refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
-        const std::optional<Fps> renderRate =
-                mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
-
-        const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
-        const auto gameMode = getGameMode();
-
-        if (presentFence->isValid()) {
-            mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                                  refreshRate, renderRate, vote, gameMode);
-            mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
-                                               presentFence,
-                                               FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.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 refresh
-            // timestamp instead.
-            const nsecs_t actualPresentTime = display->getRefreshTimestamp();
-            mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                                 refreshRate, renderRate, vote, gameMode);
-            mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
-                                                   mCurrentFrameNumber, actualPresentTime,
-                                                   FrameTracer::FrameEvent::PRESENT_FENCE);
-            mFrameTracker.setActualPresentTime(actualPresentTime);
-        }
-    }
-
-    mFrameTracker.advanceFrame();
-    mBufferInfo.mFrameLatencyNeeded = false;
-}
-
-void BufferLayer::gatherBufferInfo() {
-    mBufferInfo.mPixelFormat =
-            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
-    mBufferInfo.mFrameLatencyNeeded = true;
-}
-
-bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const {
-    // If this is not a valid vsync for the layer's uid, return and try again later
-    const bool isVsyncValidForUid =
-            mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
-    if (!isVsyncValidForUid) {
-        ATRACE_NAME("!isVsyncValidForUid");
-        return false;
-    }
-
-    // AutoRefresh layers and sideband streams should always be presented
-    if (getSidebandStreamChanged() || getAutoRefresh()) {
-        return true;
-    }
-
-    // If this layer doesn't have a frame is shouldn't be presented
-    if (!hasFrameUpdate()) {
-        return false;
-    }
-
-    // Defer to the derived class to decide whether the next buffer is due for
-    // presentation.
-    return isBufferDue(expectedPresentTime);
-}
-
-bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                              nsecs_t expectedPresentTime) {
-    ATRACE_CALL();
-
-    bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
-
-    if (refreshRequired) {
-        return refreshRequired;
-    }
-
-    // If the head buffer's acquire fence hasn't signaled yet, return and
-    // try again later
-    if (!fenceHasSignaled()) {
-        ATRACE_NAME("!fenceHasSignaled()");
-        mFlinger->onLayerUpdate();
-        return false;
-    }
-
-    // Capture the old state of the layer for comparisons later
-    const State& s(getDrawingState());
-    const bool oldOpacity = isOpaque(s);
-
-    BufferInfo oldBufferInfo = mBufferInfo;
-
-    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
-    if (err != NO_ERROR) {
-        return false;
-    }
-
-    err = updateActiveBuffer();
-    if (err != NO_ERROR) {
-        return false;
-    }
-
-    err = updateFrameNumber();
-    if (err != NO_ERROR) {
-        return false;
-    }
-
-    gatherBufferInfo();
-
-    if (oldBufferInfo.mBuffer == nullptr) {
-        // the first time we receive a buffer, we need to trigger a
-        // geometry invalidation.
-        recomputeVisibleRegions = true;
-    }
-
-    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
-        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
-        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
-        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
-        recomputeVisibleRegions = true;
-    }
-
-    if (oldBufferInfo.mBuffer != nullptr) {
-        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-        if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
-            bufHeight != oldBufferInfo.mBuffer->getHeight()) {
-            recomputeVisibleRegions = true;
-        }
-    }
-
-    if (oldOpacity != isOpaque(s)) {
-        recomputeVisibleRegions = true;
-    }
-
-    return true;
-}
-
-bool BufferLayer::hasReadyFrame() const {
-    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
-}
-
-uint32_t BufferLayer::getEffectiveScalingMode() const {
-    return mBufferInfo.mScaleMode;
-}
-
-bool BufferLayer::isProtected() const {
-    return (mBufferInfo.mBuffer != nullptr) &&
-            (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-// As documented in libhardware header, formats in the range
-// 0x100 - 0x1FF are specific to the HAL implementation, and
-// are known to have no alpha channel
-// TODO: move definition for device-specific range into
-// hardware.h, instead of using hard-coded values here.
-#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-
-bool BufferLayer::getOpacityForFormat(PixelFormat format) {
-    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
-        return true;
-    }
-    switch (format) {
-        case PIXEL_FORMAT_RGBA_8888:
-        case PIXEL_FORMAT_BGRA_8888:
-        case PIXEL_FORMAT_RGBA_FP16:
-        case PIXEL_FORMAT_RGBA_1010102:
-        case PIXEL_FORMAT_R_8:
-            return false;
-    }
-    // in all other case, we have no blending (also for unknown formats)
-    return true;
-}
-
-bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer == nullptr) {
-        return false;
-    }
-
-    // We need filtering if the sourceCrop rectangle size does not match the
-    // displayframe rectangle size (not a 1:1 render)
-    const auto& compositionState = outputLayer->getState();
-    const auto displayFrame = compositionState.displayFrame;
-    const auto sourceCrop = compositionState.sourceCrop;
-    return sourceCrop.getHeight() != displayFrame.getHeight() ||
-            sourceCrop.getWidth() != displayFrame.getWidth();
-}
-
-bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
-                                               const ui::Transform& inverseParentTransform) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    if (outputLayer == nullptr) {
-        return false;
-    }
-
-    // We need filtering if the sourceCrop rectangle size does not match the
-    // viewport rectangle size (not a 1:1 render)
-    const auto& compositionState = outputLayer->getState();
-    const ui::Transform& displayTransform = display->getTransform();
-    const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
-    // Undo the transformation of the displayFrame so that we're back into
-    // layer-stack space.
-    const Rect frame = inverseTransform.transform(compositionState.displayFrame);
-    const FloatRect sourceCrop = compositionState.sourceCrop;
-
-    int32_t frameHeight = frame.getHeight();
-    int32_t frameWidth = frame.getWidth();
-    // If the display transform had a rotational component then undo the
-    // rotation so that the orientation matches the source crop.
-    if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
-        std::swap(frameHeight, frameWidth);
-    }
-    return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
-}
-
-Rect BufferLayer::getBufferSize(const State& s) const {
-    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
-    // we cannot determine the buffer size.
-    if ((s.sidebandStream != nullptr) ||
-        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
-        return Rect(getActiveWidth(s), getActiveHeight(s));
-    }
-
-    if (mBufferInfo.mBuffer == nullptr) {
-        return Rect::INVALID_RECT;
-    }
-
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
-    // Undo any transformations on the buffer and return the result.
-    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    return Rect(bufWidth, bufHeight);
-}
-
-FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
-    const State& s(getDrawingState());
-
-    // If we have a sideband stream, or we are scaling the buffer then return the layer size since
-    // we cannot determine the buffer size.
-    if ((s.sidebandStream != nullptr) ||
-        (getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE)) {
-        return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
-    }
-
-    if (mBufferInfo.mBuffer == nullptr) {
-        return parentBounds;
-    }
-
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
-    // Undo any transformations on the buffer and return the result.
-    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    return FloatRect(0, 0, bufWidth, bufHeight);
-}
-
-void BufferLayer::latchAndReleaseBuffer() {
-    if (hasReadyFrame()) {
-        bool ignored = false;
-        latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
-    }
-    releasePendingBuffer(systemTime());
-}
-
-PixelFormat BufferLayer::getPixelFormat() const {
-    return mBufferInfo.mPixelFormat;
-}
-
-bool BufferLayer::getTransformToDisplayInverse() const {
-    return mBufferInfo.mTransformToDisplayInverse;
-}
-
-Rect BufferLayer::getBufferCrop() const {
-    // this is the crop rectangle that applies to the buffer
-    // itself (as opposed to the window)
-    if (!mBufferInfo.mCrop.isEmpty()) {
-        // if the buffer crop is defined, we use that
-        return mBufferInfo.mCrop;
-    } else if (mBufferInfo.mBuffer != nullptr) {
-        // otherwise we use the whole buffer
-        return mBufferInfo.mBuffer->getBounds();
-    } else {
-        // if we don't have a buffer yet, we use an empty/invalid crop
-        return Rect();
-    }
-}
-
-uint32_t BufferLayer::getBufferTransform() const {
-    return mBufferInfo.mTransform;
-}
-
-ui::Dataspace BufferLayer::getDataSpace() const {
-    return mBufferInfo.mDataspace;
-}
-
-ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) {
-    ui::Dataspace updatedDataspace = dataspace;
-    // translate legacy dataspaces to modern dataspaces
-    switch (dataspace) {
-        case ui::Dataspace::SRGB:
-            updatedDataspace = ui::Dataspace::V0_SRGB;
-            break;
-        case ui::Dataspace::SRGB_LINEAR:
-            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
-            break;
-        case ui::Dataspace::JFIF:
-            updatedDataspace = ui::Dataspace::V0_JFIF;
-            break;
-        case ui::Dataspace::BT601_625:
-            updatedDataspace = ui::Dataspace::V0_BT601_625;
-            break;
-        case ui::Dataspace::BT601_525:
-            updatedDataspace = ui::Dataspace::V0_BT601_525;
-            break;
-        case ui::Dataspace::BT709:
-            updatedDataspace = ui::Dataspace::V0_BT709;
-            break;
-        default:
-            break;
-    }
-
-    return updatedDataspace;
-}
-
-sp<GraphicBuffer> BufferLayer::getBuffer() const {
-    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
-}
-
-void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
-    GLConsumer::computeTransformMatrix(outMatrix,
-                                       mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()
-                                                           : nullptr,
-                                       mBufferInfo.mCrop, mBufferInfo.mTransform, filteringEnabled);
-}
-
-void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
-    Layer::setInitialValuesForClone(clonedFrom);
-
-    sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get());
-    mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
-    mPotentialCursor = bufferClonedFrom->mPotentialCursor;
-    mProtectedByApp = bufferClonedFrom->mProtectedByApp;
-
-    updateCloneBufferInfo();
-}
-
-void BufferLayer::updateCloneBufferInfo() {
-    if (!isClone() || !isClonedFromAlive()) {
-        return;
-    }
-
-    sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get());
-    mBufferInfo = clonedFrom->mBufferInfo;
-    mSidebandStream = clonedFrom->mSidebandStream;
-    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
-    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
-    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
-
-    // After buffer info is updated, the drawingState from the real layer needs to be copied into
-    // the cloned. This is because some properties of drawingState can change when latchBuffer is
-    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
-    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
-    // the cloned drawingState again.
-    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
-    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
-    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
-    WindowInfo tmpInputInfo = mDrawingState.inputInfo;
-
-    cloneDrawingState(clonedFrom.get());
-
-    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
-    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
-    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
-    mDrawingState.inputInfo = tmpInputInfo;
-}
-
-void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
-    mTransformHint = getFixedTransformHint();
-    if (mTransformHint == ui::Transform::ROT_INVALID) {
-        mTransformHint = displayTransformHint;
-    }
-}
-
-bool BufferLayer::bufferNeedsFiltering() const {
-    return isFixedSize();
-}
-
-const std::shared_ptr<renderengine::ExternalTexture>& BufferLayer::getExternalTexture() const {
-    return mBufferInfo.mBuffer;
-}
-
-} // namespace android
-
-#if defined(__gl_h_)
-#error "don't include gl/gl.h in this file"
-#endif
-
-#if defined(__gl2_h_)
-#error "don't include gl2/gl2.h in this file"
-#endif
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
deleted file mode 100644
index 4c70eb5..0000000
--- a/services/surfaceflinger/BufferLayer.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2017 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 <sys/types.h>
-#include <cstdint>
-#include <list>
-
-#include <gui/ISurfaceComposerClient.h>
-#include <gui/LayerState.h>
-#include <renderengine/Image.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
-#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE
-#include <ui/FrameStats.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/PixelFormat.h>
-#include <ui/Region.h>
-#include <utils/RefBase.h>
-#include <utils/String8.h>
-#include <utils/Timers.h>
-
-#include "BufferLayerConsumer.h"
-#include "Client.h"
-#include "DisplayHardware/HWComposer.h"
-#include "FrameTimeline.h"
-#include "FrameTracker.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-
-class BufferLayer : public Layer {
-public:
-    explicit BufferLayer(const LayerCreationArgs& args);
-    virtual ~BufferLayer() override;
-
-    // Implements Layer.
-    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
-    compositionengine::LayerFECompositionState* editCompositionState() override;
-
-    // If we have received a new buffer this frame, we will pass its surface
-    // damage down to hardware composer. Otherwise, we must send a region with
-    // one empty rect.
-    void useSurfaceDamage() override;
-    void useEmptyDamage() override;
-
-    bool isOpaque(const Layer::State& s) const override;
-    bool canReceiveInput() const override;
-
-    // isVisible - true if this layer is visible, false otherwise
-    bool isVisible() const override;
-
-    // isProtected - true if the layer may contain protected content in the
-    // GRALLOC_USAGE_PROTECTED sense.
-    bool isProtected() const override;
-
-    // isFixedSize - true if content has a fixed size
-    bool isFixedSize() const override;
-
-    bool usesSourceCrop() const override;
-
-    bool isHdrY410() const override;
-
-    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
-                           const std::shared_ptr<FenceTime>& presentFence,
-                           const CompositorTiming&) override;
-
-    // latchBuffer - called each time the screen is redrawn and returns whether
-    // the visible regions need to be recomputed (this is a fairly heavy
-    // operation, so this should be set only if needed). Typically this is used
-    // to figure out if the content or size of a surface has changed.
-    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                     nsecs_t expectedPresentTime) override;
-    bool hasReadyFrame() const override;
-
-    // Returns the current scaling mode
-    uint32_t getEffectiveScalingMode() const override;
-
-    // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
-    // This is used if the buffer is just latched and releases to free up the buffer
-    // and will not be shown on screen.
-    // Should only be called on the main thread.
-    void latchAndReleaseBuffer() override;
-
-    bool getTransformToDisplayInverse() const override;
-
-    Rect getBufferCrop() const override;
-
-    uint32_t getBufferTransform() const override;
-
-    ui::Dataspace getDataSpace() const override;
-
-    sp<GraphicBuffer> getBuffer() const override;
-    const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const override;
-
-    ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
-
-    // Returns true if the transformed buffer size does not match the layer size and we need
-    // to apply filtering.
-    virtual bool bufferNeedsFiltering() const;
-
-protected:
-    struct BufferInfo {
-        nsecs_t mDesiredPresentTime;
-        std::shared_ptr<FenceTime> mFenceTime;
-        sp<Fence> mFence;
-        uint32_t mTransform{0};
-        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
-        Rect mCrop;
-        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
-        Region mSurfaceDamage;
-        HdrMetadata mHdrMetadata;
-        int mApi;
-        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
-        bool mTransformToDisplayInverse{false};
-
-        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
-        uint64_t mFrameNumber;
-        int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
-
-        bool mFrameLatencyNeeded{false};
-    };
-
-    BufferInfo mBufferInfo;
-    virtual void gatherBufferInfo() = 0;
-
-    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-
-    /*
-     * compositionengine::LayerFE overrides
-     */
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    bool onPreComposition(nsecs_t) override;
-    void preparePerFrameCompositionState() override;
-
-    static bool getOpacityForFormat(PixelFormat format);
-
-    // from graphics API
-    const uint32_t mTextureName;
-    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
-    void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-    void updateCloneBufferInfo() override;
-    uint64_t mPreviousFrameNumber = 0;
-
-    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
-    // Transform hint provided to the producer. This must be accessed holding
-    // the mStateLock.
-    ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
-
-    bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
-    bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
-
-    // Returns true if the next buffer should be presented at the expected present time
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const;
-
-    // Returns true if the next buffer should be presented at the expected present time,
-    // overridden by BufferStateLayer and BufferQueueLayer for implementation
-    // specific logic
-    virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
-
-    std::atomic<bool> mSidebandStreamChanged{false};
-
-private:
-    virtual bool fenceHasSignaled() const = 0;
-    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
-
-    // Latch sideband stream and returns true if the dirty region should be updated.
-    virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
-
-    virtual bool hasFrameUpdate() const = 0;
-
-    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                                    nsecs_t expectedPresentTime) = 0;
-
-    virtual status_t updateActiveBuffer() = 0;
-    virtual status_t updateFrameNumber() = 0;
-
-    // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
-    // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
-    // detection.
-    bool needsInputInfo() const override { return !mPotentialCursor; }
-
-    // Returns true if this layer requires filtering
-    bool needsFiltering(const DisplayDevice*) const override;
-    bool needsFilteringForScreenshots(const DisplayDevice*,
-                                      const ui::Transform& inverseParentTransform) const override;
-
-    // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
-    // and its parent layer is not bounded
-    Rect getBufferSize(const State& s) const override;
-
-    PixelFormat getPixelFormat() const;
-
-    // Computes the transform matrix using the setFilteringEnabled to determine whether the
-    // transform matrix should be computed for use with bilinear filtering.
-    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
-
-    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
-    FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
deleted file mode 100644
index a1035f0..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2010 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"
-
-#undef LOG_TAG
-#define LOG_TAG "BufferLayerConsumer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-//#define LOG_NDEBUG 0
-
-#include "BufferLayerConsumer.h"
-#include "Layer.h"
-#include "Scheduler/VsyncController.h"
-
-#include <inttypes.h>
-
-#include <cutils/compiler.h>
-
-#include <hardware/hardware.h>
-
-#include <math/mat4.h>
-
-#include <gui/BufferItem.h>
-#include <gui/GLConsumer.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/impl/ExternalTexture.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// Macros for including the BufferLayerConsumer name in log messages
-#define BLC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
-#define BLC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
-// #define BLC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
-#define BLC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
-#define BLC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
-
-static const mat4 mtxIdentity;
-
-BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq,
-                                         renderengine::RenderEngine& engine, uint32_t tex,
-                                         Layer* layer)
-      : ConsumerBase(bq, false),
-        mCurrentCrop(Rect::EMPTY_RECT),
-        mCurrentTransform(0),
-        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
-        mCurrentFence(Fence::NO_FENCE),
-        mCurrentTimestamp(0),
-        mCurrentDataSpace(ui::Dataspace::UNKNOWN),
-        mCurrentFrameNumber(0),
-        mCurrentTransformToDisplayInverse(false),
-        mCurrentSurfaceDamage(),
-        mCurrentApi(0),
-        mDefaultWidth(1),
-        mDefaultHeight(1),
-        mFilteringEnabled(true),
-        mRE(engine),
-        mTexName(tex),
-        mLayer(layer),
-        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) {
-    BLC_LOGV("BufferLayerConsumer");
-
-    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
-
-    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
-}
-
-status_t BufferLayerConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        BLC_LOGE("setDefaultBufferSize: BufferLayerConsumer is abandoned!");
-        return NO_INIT;
-    }
-    mDefaultWidth = w;
-    mDefaultHeight = h;
-    return mConsumer->setDefaultBufferSize(w, h);
-}
-
-void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedListener>& listener) {
-    setFrameAvailableListener(listener);
-    Mutex::Autolock lock(mMutex);
-    mContentsChangedListener = listener;
-}
-
-status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
-                                             bool* autoRefresh, bool* queuedBuffer,
-                                             uint64_t maxFrameNumber) {
-    ATRACE_CALL();
-    BLC_LOGV("updateTexImage");
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        BLC_LOGE("updateTexImage: BufferLayerConsumer is abandoned!");
-        return NO_INIT;
-    }
-
-    BufferItem item;
-
-    // Acquire the next buffer.
-    // In asynchronous mode the list is guaranteed to be one buffer
-    // deep, while in synchronous mode we use the oldest buffer.
-    status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
-    if (err != NO_ERROR) {
-        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-            err = NO_ERROR;
-        } else if (err == BufferQueue::PRESENT_LATER) {
-            // return the error, without logging
-        } else {
-            BLC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
-        }
-        return err;
-    }
-
-    if (autoRefresh) {
-        *autoRefresh = item.mAutoRefresh;
-    }
-
-    if (queuedBuffer) {
-        *queuedBuffer = item.mQueuedBuffer;
-    }
-
-    // We call the rejecter here, in case the caller has a reason to
-    // not accept this buffer.  This is used by SurfaceFlinger to
-    // reject buffers which have the wrong size
-    int slot = item.mSlot;
-    if (rejecter && rejecter->reject(mSlots[slot].mGraphicBuffer, item)) {
-        releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer);
-        return BUFFER_REJECTED;
-    }
-
-    // Release the previous buffer.
-    err = updateAndReleaseLocked(item, &mPendingRelease);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    return err;
-}
-
-void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) {
-    if (!fence->isValid()) {
-        return;
-    }
-
-    auto slot = mPendingRelease.isPending ? mPendingRelease.currentTexture : mCurrentTexture;
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
-        return;
-    }
-
-    auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer
-                                            : mCurrentTextureBuffer->getBuffer();
-    auto err = addReleaseFence(slot, buffer, fence);
-    if (err != OK) {
-        BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err);
-    }
-}
-
-bool BufferLayerConsumer::releasePendingBuffer() {
-    if (!mPendingRelease.isPending) {
-        BLC_LOGV("Pending buffer already released");
-        return false;
-    }
-    BLC_LOGV("Releasing pending buffer");
-    Mutex::Autolock lock(mMutex);
-    status_t result =
-            releaseBufferLocked(mPendingRelease.currentTexture, mPendingRelease.graphicBuffer);
-    if (result < NO_ERROR) {
-        BLC_LOGE("releasePendingBuffer failed: %s (%d)", strerror(-result), result);
-    }
-    mPendingRelease = PendingRelease();
-    return true;
-}
-
-sp<Fence> BufferLayerConsumer::getPrevFinalReleaseFence() const {
-    Mutex::Autolock lock(mMutex);
-    return ConsumerBase::mPrevFinalReleaseFence;
-}
-
-status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
-                                                  uint64_t maxFrameNumber) {
-    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
-    if (err != NO_ERROR) {
-        return err;
-    }
-
-    // If item->mGraphicBuffer is not null, this buffer has not been acquired
-    // before, so we need to clean up old references.
-    if (item->mGraphicBuffer != nullptr) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        if (mImages[item->mSlot] == nullptr || mImages[item->mSlot]->getBuffer() == nullptr ||
-            mImages[item->mSlot]->getBuffer()->getId() != item->mGraphicBuffer->getId()) {
-            mImages[item->mSlot] = std::make_shared<
-                    renderengine::impl::ExternalTexture>(item->mGraphicBuffer, mRE,
-                                                         renderengine::impl::ExternalTexture::
-                                                                 Usage::READABLE);
-        }
-    }
-
-    return NO_ERROR;
-}
-
-status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item,
-                                                     PendingRelease* pendingRelease) {
-    status_t err = NO_ERROR;
-
-    int slot = item.mSlot;
-
-    BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
-             (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->getBuffer() != nullptr)
-                     ? mCurrentTextureBuffer->getBuffer()->handle
-                     : 0,
-             slot, mSlots[slot].mGraphicBuffer->handle);
-
-    // Hang onto the pointer so that it isn't freed in the call to
-    // releaseBufferLocked() if we're in shared buffer mode and both buffers are
-    // the same.
-
-    std::shared_ptr<renderengine::ExternalTexture> nextTextureBuffer;
-    {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        nextTextureBuffer = mImages[slot];
-    }
-
-    // release old buffer
-    if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
-        if (pendingRelease == nullptr) {
-            status_t status =
-                    releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->getBuffer());
-            if (status < NO_ERROR) {
-                BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
-                         status);
-                err = status;
-                // keep going, with error raised [?]
-            }
-        } else {
-            pendingRelease->currentTexture = mCurrentTexture;
-            pendingRelease->graphicBuffer = mCurrentTextureBuffer->getBuffer();
-            pendingRelease->isPending = true;
-        }
-    }
-
-    // Update the BufferLayerConsumer state.
-    mCurrentTexture = slot;
-    mCurrentTextureBuffer = nextTextureBuffer;
-    mCurrentCrop = item.mCrop;
-    mCurrentTransform = item.mTransform;
-    mCurrentScalingMode = item.mScalingMode;
-    mCurrentTimestamp = item.mTimestamp;
-    mCurrentDataSpace = static_cast<ui::Dataspace>(item.mDataSpace);
-    mCurrentHdrMetadata = item.mHdrMetadata;
-    mCurrentFence = item.mFence;
-    mCurrentFenceTime = item.mFenceTime;
-    mCurrentFrameNumber = item.mFrameNumber;
-    mCurrentTransformToDisplayInverse = item.mTransformToDisplayInverse;
-    mCurrentSurfaceDamage = item.mSurfaceDamage;
-    mCurrentApi = item.mApi;
-
-    computeCurrentTransformMatrixLocked();
-
-    return err;
-}
-
-void BufferLayerConsumer::getTransformMatrix(float mtx[16]) {
-    Mutex::Autolock lock(mMutex);
-    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
-}
-
-void BufferLayerConsumer::setFilteringEnabled(bool enabled) {
-    Mutex::Autolock lock(mMutex);
-    if (mAbandoned) {
-        BLC_LOGE("setFilteringEnabled: BufferLayerConsumer is abandoned!");
-        return;
-    }
-    bool needsRecompute = mFilteringEnabled != enabled;
-    mFilteringEnabled = enabled;
-
-    if (needsRecompute && mCurrentTextureBuffer == nullptr) {
-        BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr");
-    }
-
-    if (needsRecompute && mCurrentTextureBuffer != nullptr) {
-        computeCurrentTransformMatrixLocked();
-    }
-}
-
-void BufferLayerConsumer::computeCurrentTransformMatrixLocked() {
-    BLC_LOGV("computeCurrentTransformMatrixLocked");
-    if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->getBuffer() == nullptr) {
-        BLC_LOGD("computeCurrentTransformMatrixLocked: "
-                 "mCurrentTextureBuffer is nullptr");
-    }
-    GLConsumer::computeTransformMatrix(mCurrentTransformMatrix,
-                                       mCurrentTextureBuffer == nullptr
-                                               ? nullptr
-                                               : mCurrentTextureBuffer->getBuffer(),
-                                       getCurrentCropLocked(), mCurrentTransform,
-                                       mFilteringEnabled);
-}
-
-nsecs_t BufferLayerConsumer::getTimestamp() {
-    BLC_LOGV("getTimestamp");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTimestamp;
-}
-
-ui::Dataspace BufferLayerConsumer::getCurrentDataSpace() {
-    BLC_LOGV("getCurrentDataSpace");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentDataSpace;
-}
-
-const HdrMetadata& BufferLayerConsumer::getCurrentHdrMetadata() const {
-    BLC_LOGV("getCurrentHdrMetadata");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentHdrMetadata;
-}
-
-uint64_t BufferLayerConsumer::getFrameNumber() {
-    BLC_LOGV("getFrameNumber");
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFrameNumber;
-}
-
-bool BufferLayerConsumer::getTransformToDisplayInverse() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTransformToDisplayInverse;
-}
-
-const Region& BufferLayerConsumer::getSurfaceDamage() const {
-    return mCurrentSurfaceDamage;
-}
-
-void BufferLayerConsumer::mergeSurfaceDamage(const Region& damage) {
-    if (damage.bounds() == Rect::INVALID_RECT ||
-        mCurrentSurfaceDamage.bounds() == Rect::INVALID_RECT) {
-        mCurrentSurfaceDamage = Region::INVALID_REGION;
-    } else {
-        mCurrentSurfaceDamage |= damage;
-    }
-}
-
-int BufferLayerConsumer::getCurrentApi() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentApi;
-}
-
-std::shared_ptr<renderengine::ExternalTexture> BufferLayerConsumer::getCurrentBuffer(
-        int* outSlot, sp<Fence>* outFence) const {
-    Mutex::Autolock lock(mMutex);
-
-    if (outSlot != nullptr) {
-        *outSlot = mCurrentTexture;
-    }
-
-    if (outFence != nullptr) {
-        *outFence = mCurrentFence;
-    }
-
-    return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer;
-}
-
-Rect BufferLayerConsumer::getCurrentCrop() const {
-    Mutex::Autolock lock(mMutex);
-    return getCurrentCropLocked();
-}
-
-Rect BufferLayerConsumer::getCurrentCropLocked() const {
-    uint32_t width = mDefaultWidth;
-    uint32_t height = mDefaultHeight;
-    // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
-    // to scale and crop correctly.
-    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
-        width = mDefaultHeight;
-        height = mDefaultWidth;
-    }
-
-    return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
-            ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
-            : mCurrentCrop;
-}
-
-uint32_t BufferLayerConsumer::getCurrentTransform() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentTransform;
-}
-
-uint32_t BufferLayerConsumer::getCurrentScalingMode() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentScalingMode;
-}
-
-sp<Fence> BufferLayerConsumer::getCurrentFence() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFence;
-}
-
-std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentFenceTime;
-}
-
-void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
-    BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
-    std::lock_guard<std::mutex> lock(mImagesMutex);
-    if (slotIndex == mCurrentTexture) {
-        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
-    }
-    mImages[slotIndex] = nullptr;
-    ConsumerBase::freeBufferLocked(slotIndex);
-}
-
-void BufferLayerConsumer::onDisconnect() {
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // Nothing to do if we're already abandoned.
-        return;
-    }
-
-    mLayer->onDisconnect();
-}
-
-void BufferLayerConsumer::onSidebandStreamChanged() {
-    [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
-    {
-        Mutex::Autolock lock(mFrameAvailableMutex);
-        unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
-    }
-    sp<ContentsChangedListener> listener;
-    { // scope for the lock
-        Mutex::Autolock lock(mMutex);
-        ALOG_ASSERT(unsafeFrameAvailableListener == mContentsChangedListener.unsafe_get());
-        listener = mContentsChangedListener.promote();
-    }
-
-    if (listener != nullptr) {
-        listener->onSidebandStreamChanged();
-    }
-}
-
-void BufferLayerConsumer::onBufferAvailable(const BufferItem& item) {
-    if (item.mGraphicBuffer != nullptr && item.mSlot != BufferQueue::INVALID_BUFFER_SLOT) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        const std::shared_ptr<renderengine::ExternalTexture>& oldImage = mImages[item.mSlot];
-        if (oldImage == nullptr || oldImage->getBuffer() == nullptr ||
-            oldImage->getBuffer()->getId() != item.mGraphicBuffer->getId()) {
-            mImages[item.mSlot] = std::make_shared<
-                    renderengine::impl::ExternalTexture>(item.mGraphicBuffer, mRE,
-                                                         renderengine::impl::ExternalTexture::
-                                                                 Usage::READABLE);
-        }
-    }
-}
-
-void BufferLayerConsumer::abandonLocked() {
-    BLC_LOGV("abandonLocked");
-    mCurrentTextureBuffer = nullptr;
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        std::lock_guard<std::mutex> lock(mImagesMutex);
-        mImages[i] = nullptr;
-    }
-    ConsumerBase::abandonLocked();
-}
-
-status_t BufferLayerConsumer::setConsumerUsageBits(uint64_t usage) {
-    return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
-}
-
-void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const {
-    result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
-                        "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
-                        prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
-                        mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
-                        mCurrentTransform);
-
-    ConsumerBase::dumpLocked(result, prefix);
-}
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
deleted file mode 100644
index 23ad2a3..0000000
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_BUFFERLAYERCONSUMER_H
-#define ANDROID_BUFFERLAYERCONSUMER_H
-
-#include <android-base/thread_annotations.h>
-#include <gui/BufferQueueDefs.h>
-#include <gui/ConsumerBase.h>
-#include <gui/HdrMetadata.h>
-#include <renderengine/ExternalTexture.h>
-#include <ui/FenceTime.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
-#include <ui/Region.h>
-#include <utils/String8.h>
-#include <utils/Vector.h>
-#include <utils/threads.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class Layer;
-class String8;
-
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
-/*
- * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue,
- * and makes them available to RenderEngine as a texture.
- *
- * A typical usage pattern is to call updateTexImage() when a new frame is
- * desired.  If a new frame is available, the frame is latched.  If not, the
- * previous contents are retained.  The texture is attached and updated after
- * bindTextureImage() is called.
- *
- * All calls to updateTexImage must be made with RenderEngine being current.
- * The texture is attached to the TEXTURE_EXTERNAL texture target.
- */
-class BufferLayerConsumer : public ConsumerBase {
-public:
-    static const status_t BUFFER_REJECTED = UNKNOWN_ERROR + 8;
-
-    class BufferRejecter {
-        friend class BufferLayerConsumer;
-        virtual bool reject(const sp<GraphicBuffer>& buf, const BufferItem& item) = 0;
-
-    protected:
-        virtual ~BufferRejecter() {}
-    };
-
-    struct ContentsChangedListener : public FrameAvailableListener {
-        virtual void onSidebandStreamChanged() = 0;
-    };
-
-    // BufferLayerConsumer constructs a new BufferLayerConsumer object.  The
-    // tex parameter indicates the name of the RenderEngine texture to which
-    // images are to be streamed.
-    BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine,
-                        uint32_t tex, Layer* layer);
-
-    // Sets the contents changed listener. This should be used instead of
-    // ConsumerBase::setFrameAvailableListener().
-    void setContentsChangedListener(const wp<ContentsChangedListener>& listener);
-
-    // updateTexImage acquires the most recently queued buffer, and sets the
-    // image contents of the target texture to it.
-    //
-    // This call may only be made while RenderEngine is current.
-    //
-    // This calls doFenceWait to ensure proper synchronization unless native
-    // fence is supported.
-    //
-    // Unlike the GLConsumer version, this version takes a functor that may be
-    // used to reject the newly acquired buffer.  It also does not bind the
-    // RenderEngine texture until bindTextureImage is called.
-    status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
-                            bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber);
-
-    // setReleaseFence stores a fence that will signal when the current buffer
-    // is no longer being read. This fence will be returned to the producer
-    // when the current buffer is released by updateTexImage(). Multiple
-    // fences can be set for a given buffer; they will be merged into a single
-    // union fence.
-    void setReleaseFence(const sp<Fence>& fence);
-
-    bool releasePendingBuffer();
-
-    sp<Fence> getPrevFinalReleaseFence() const;
-
-    // See GLConsumer::getTransformMatrix.
-    void getTransformMatrix(float mtx[16]);
-
-    // getTimestamp retrieves the timestamp associated with the texture image
-    // set by the most recent call to updateTexImage.
-    //
-    // The timestamp is in nanoseconds, and is monotonically increasing. Its
-    // other semantics (zero point, etc) are source-dependent and should be
-    // documented by the source.
-    int64_t getTimestamp();
-
-    // getDataSpace retrieves the DataSpace associated with the texture image
-    // set by the most recent call to updateTexImage.
-    ui::Dataspace getCurrentDataSpace();
-
-    // getCurrentHdrMetadata retrieves the HDR metadata associated with the
-    // texture image set by the most recent call to updateTexImage.
-    const HdrMetadata& getCurrentHdrMetadata() const;
-
-    // getFrameNumber retrieves the frame number associated with the texture
-    // image set by the most recent call to updateTexImage.
-    //
-    // The frame number is an incrementing counter set to 0 at the creation of
-    // the BufferQueue associated with this consumer.
-    uint64_t getFrameNumber();
-
-    bool getTransformToDisplayInverse() const;
-
-    // must be called from SF main thread
-    const Region& getSurfaceDamage() const;
-
-    // Merge the given damage region into the current damage region value.
-    void mergeSurfaceDamage(const Region& damage);
-
-    // getCurrentApi retrieves the API which queues the current buffer.
-    int getCurrentApi() const;
-
-    // See GLConsumer::setDefaultBufferSize.
-    status_t setDefaultBufferSize(uint32_t width, uint32_t height);
-
-    // setFilteringEnabled sets whether the transform matrix should be computed
-    // for use with bilinear filtering.
-    void setFilteringEnabled(bool enabled);
-
-    // getCurrentBuffer returns the buffer associated with the current image.
-    // When outSlot is not nullptr, the current buffer slot index is also
-    // returned. Simiarly, when outFence is not nullptr, the current output
-    // fence is returned.
-    std::shared_ptr<renderengine::ExternalTexture> getCurrentBuffer(
-            int* outSlot = nullptr, sp<Fence>* outFence = nullptr) const;
-
-    // getCurrentCrop returns the cropping rectangle of the current buffer.
-    Rect getCurrentCrop() const;
-
-    // getCurrentTransform returns the transform of the current buffer.
-    uint32_t getCurrentTransform() const;
-
-    // getCurrentScalingMode returns the scaling mode of the current buffer.
-    uint32_t getCurrentScalingMode() const;
-
-    // getCurrentFence returns the fence indicating when the current buffer is
-    // ready to be read from.
-    sp<Fence> getCurrentFence() const;
-
-    // getCurrentFence returns the FenceTime indicating when the current
-    // buffer is ready to be read from.
-    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
-
-    // setConsumerUsageBits overrides the ConsumerBase method to OR
-    // DEFAULT_USAGE_FLAGS to usage.
-    status_t setConsumerUsageBits(uint64_t usage);
-    void onBufferAvailable(const BufferItem& item) EXCLUDES(mImagesMutex);
-
-protected:
-    // abandonLocked overrides the ConsumerBase method to clear
-    // mCurrentTextureImage in addition to the ConsumerBase behavior.
-    virtual void abandonLocked() EXCLUDES(mImagesMutex);
-
-    // dumpLocked overrides the ConsumerBase method to dump BufferLayerConsumer-
-    // specific info in addition to the ConsumerBase behavior.
-    virtual void dumpLocked(String8& result, const char* prefix) const;
-
-    // See ConsumerBase::acquireBufferLocked
-    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
-                                         uint64_t maxFrameNumber = 0) override
-            EXCLUDES(mImagesMutex);
-
-    bool canUseImageCrop(const Rect& crop) const;
-
-    struct PendingRelease {
-        PendingRelease() : isPending(false), currentTexture(-1), graphicBuffer() {}
-
-        bool isPending;
-        int currentTexture;
-        sp<GraphicBuffer> graphicBuffer;
-    };
-
-    // This releases the buffer in the slot referenced by mCurrentTexture,
-    // then updates state to refer to the BufferItem, which must be a
-    // newly-acquired buffer. If pendingRelease is not null, the parameters
-    // which would have been passed to releaseBufferLocked upon the successful
-    // completion of the method will instead be returned to the caller, so that
-    // it may call releaseBufferLocked itself later.
-    status_t updateAndReleaseLocked(const BufferItem& item,
-                                    PendingRelease* pendingRelease = nullptr)
-            EXCLUDES(mImagesMutex);
-
-private:
-    // Utility class for managing GraphicBuffer references into renderengine
-    class Image {
-    public:
-        Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine);
-        virtual ~Image();
-        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
-
-    private:
-        // mGraphicBuffer is the buffer that was used to create this image.
-        sp<GraphicBuffer> mGraphicBuffer;
-        // Back-reference into renderengine to initiate cleanup.
-        renderengine::RenderEngine& mRE;
-        DISALLOW_COPY_AND_ASSIGN(Image);
-    };
-
-    // freeBufferLocked frees up the given buffer slot. If the slot has been
-    // initialized this will release the reference to the GraphicBuffer in
-    // that slot.  Otherwise it has no effect.
-    //
-    // This method must be called with mMutex locked.
-    virtual void freeBufferLocked(int slotIndex) EXCLUDES(mImagesMutex);
-
-    // IConsumerListener interface
-    void onDisconnect() override;
-    void onSidebandStreamChanged() override;
-    void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {}
-
-    // computeCurrentTransformMatrixLocked computes the transform matrix for the
-    // current texture.  It uses mCurrentTransform and the current GraphicBuffer
-    // to compute this matrix and stores it in mCurrentTransformMatrix.
-    // mCurrentTextureImage must not be nullptr.
-    void computeCurrentTransformMatrixLocked();
-
-    // getCurrentCropLocked returns the cropping rectangle of the current buffer.
-    Rect getCurrentCropLocked() const;
-
-    // The default consumer usage flags that BufferLayerConsumer always sets on its
-    // BufferQueue instance; these will be OR:d with any additional flags passed
-    // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always
-    // consume buffers as hardware textures.
-    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
-
-    // mCurrentTextureBuffer is the buffer containing the current texture. It's
-    // possible that this buffer is not associated with any buffer slot, so we
-    // must track it separately in order to support the getCurrentBuffer method.
-    std::shared_ptr<renderengine::ExternalTexture> mCurrentTextureBuffer;
-
-    // mCurrentCrop is the crop rectangle that applies to the current texture.
-    // It gets set each time updateTexImage is called.
-    Rect mCurrentCrop;
-
-    // mCurrentTransform is the transform identifier for the current texture. It
-    // gets set each time updateTexImage is called.
-    uint32_t mCurrentTransform;
-
-    // mCurrentScalingMode is the scaling mode for the current texture. It gets
-    // set each time updateTexImage is called.
-    uint32_t mCurrentScalingMode;
-
-    // mCurrentFence is the fence received from BufferQueue in updateTexImage.
-    sp<Fence> mCurrentFence;
-
-    // The FenceTime wrapper around mCurrentFence.
-    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
-
-    // mCurrentTransformMatrix is the transform matrix for the current texture.
-    // It gets computed by computeTransformMatrix each time updateTexImage is
-    // called.
-    float mCurrentTransformMatrix[16];
-
-    // mCurrentTimestamp is the timestamp for the current texture. It
-    // gets set each time updateTexImage is called.
-    int64_t mCurrentTimestamp;
-
-    // mCurrentDataSpace is the dataspace for the current texture. It
-    // gets set each time updateTexImage is called.
-    ui::Dataspace mCurrentDataSpace;
-
-    // mCurrentHdrMetadata is the HDR metadata for the current texture. It
-    // gets set each time updateTexImage is called.
-    HdrMetadata mCurrentHdrMetadata;
-
-    // mCurrentFrameNumber is the frame counter for the current texture.
-    // It gets set each time updateTexImage is called.
-    uint64_t mCurrentFrameNumber;
-
-    // Indicates this buffer must be transformed by the inverse transform of the screen
-    // it is displayed onto. This is applied after BufferLayerConsumer::mCurrentTransform.
-    // This must be set/read from SurfaceFlinger's main thread.
-    bool mCurrentTransformToDisplayInverse;
-
-    // The portion of this surface that has changed since the previous frame
-    Region mCurrentSurfaceDamage;
-
-    int mCurrentApi;
-
-    uint32_t mDefaultWidth, mDefaultHeight;
-
-    // mFilteringEnabled indicates whether the transform matrix is computed for
-    // use with bilinear filtering. It defaults to true and is changed by
-    // setFilteringEnabled().
-    bool mFilteringEnabled;
-
-    renderengine::RenderEngine& mRE;
-
-    // mTexName is the name of the RenderEngine texture to which streamed
-    // images will be bound when bindTexImage is called. It is set at
-    // construction time.
-    const uint32_t mTexName;
-
-    // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
-    Layer* mLayer GUARDED_BY(mMutex);
-
-    wp<ContentsChangedListener> mContentsChangedListener;
-
-    // mCurrentTexture is the buffer slot index of the buffer that is currently
-    // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT,
-    // indicating that no buffer slot is currently bound to the texture. Note,
-    // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
-    // that no buffer is bound to the texture. A call to setBufferCount will
-    // reset mCurrentTexture to INVALID_BUFFER_SLOT.
-    int mCurrentTexture;
-
-    // Shadow buffer cache for cleaning up renderengine references.
-    std::shared_ptr<renderengine::ExternalTexture>
-            mImages[BufferQueueDefs::NUM_BUFFER_SLOTS] GUARDED_BY(mImagesMutex);
-
-    // Separate mutex guarding the shadow buffer cache.
-    // mImagesMutex can be manipulated with binder threads (e.g. onBuffersAllocated)
-    // which is contentious enough that we can't just use mMutex.
-    mutable std::mutex mImagesMutex;
-
-    // A release that is pending on the receipt of a new release fence from
-    // presentDisplay
-    PendingRelease mPendingRelease;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_BUFFERLAYERCONSUMER_H
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
deleted file mode 100644
index bee4de3..0000000
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#undef LOG_TAG
-#define LOG_TAG "BufferQueueLayer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "BufferQueueLayer.h"
-
-#include <compositionengine/LayerFECompositionState.h>
-#include <gui/BufferQueueConsumer.h>
-#include <system/window.h>
-
-#include "LayerRejecter.h"
-#include "SurfaceInterceptor.h"
-
-#include "FrameTracer/FrameTracer.h"
-#include "Scheduler/LayerHistory.h"
-#include "TimeStats/TimeStats.h"
-
-namespace android {
-using PresentState = frametimeline::SurfaceFrame::PresentState;
-
-BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
-
-BufferQueueLayer::~BufferQueueLayer() {
-    mContentsChangedListener->abandon();
-    mConsumer->abandon();
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for Layer
-// -----------------------------------------------------------------------
-
-void BufferQueueLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
-    const sp<Fence> releaseFence = futureFenceResult.get().value_or(Fence::NO_FENCE);
-    mConsumer->setReleaseFence(releaseFence);
-
-    // Prevent tracing the same release multiple times.
-    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
-        mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
-                                           std::make_shared<FenceTime>(releaseFence),
-                                           FrameTracer::FrameEvent::RELEASE_FENCE);
-        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
-    }
-}
-
-void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
-    BufferLayer::setTransformHint(displayTransformHint);
-    mConsumer->setTransformHint(mTransformHint);
-}
-
-void BufferQueueLayer::releasePendingBuffer(nsecs_t) {
-    if (!mConsumer->releasePendingBuffer()) {
-        return;
-    }
-}
-
-void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
-    mConsumer->setDefaultBufferSize(w, h);
-}
-
-int32_t BufferQueueLayer::getQueuedFrameCount() const {
-    return mQueuedFrames;
-}
-
-bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const {
-    Mutex::Autolock lock(mQueueItemLock);
-
-    const int64_t addedTime = mQueueItems[0].item.mTimestamp;
-
-    // Ignore timestamps more than a second in the future
-    const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1));
-    ALOGW_IF(!isPlausible,
-             "[%s] Timestamp %" PRId64 " seems implausible "
-             "relative to expectedPresent %" PRId64,
-             getDebugName(), addedTime, expectedPresentTime);
-
-    if (!isPlausible) {
-        mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence());
-    }
-
-    const bool isDue = addedTime < expectedPresentTime;
-    return isDue || !isPlausible;
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for BufferLayer
-// -----------------------------------------------------------------------
-
-bool BufferQueueLayer::fenceHasSignaled() const {
-    Mutex::Autolock lock(mQueueItemLock);
-
-    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-        return true;
-    }
-
-    if (!hasFrameUpdate()) {
-        return true;
-    }
-
-    if (mQueueItems[0].item.mIsDroppable) {
-        // Even though this buffer's fence may not have signaled yet, it could
-        // be replaced by another buffer before it has a chance to, which means
-        // that it's possible to get into a situation where a buffer is never
-        // able to be latched. To avoid this, grab this buffer anyway.
-        return true;
-    }
-    const bool fenceSignaled =
-            mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
-    if (!fenceSignaled) {
-        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
-                                                    TimeStats::LatchSkipReason::LateAcquire);
-    }
-
-    return fenceSignaled;
-}
-
-bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
-    Mutex::Autolock lock(mQueueItemLock);
-
-    if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
-        return true;
-    }
-
-    return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
-}
-
-bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
-    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
-    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
-
-    bool sidebandStreamChanged = true;
-    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
-        // mSidebandStreamChanged was changed to false
-        mSidebandStream = mConsumer->getSidebandStream();
-        auto* layerCompositionState = editCompositionState();
-        layerCompositionState->sidebandStream = mSidebandStream;
-        if (layerCompositionState->sidebandStream != nullptr) {
-            setTransactionFlags(eTransactionNeeded);
-            mFlinger->setTransactionFlags(eTraversalNeeded);
-        }
-        recomputeVisibleRegions = true;
-
-        return true;
-    }
-    return false;
-}
-
-bool BufferQueueLayer::hasFrameUpdate() const {
-    return mQueuedFrames > 0;
-}
-
-status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                                          nsecs_t expectedPresentTime) {
-    // This boolean is used to make sure that SurfaceFlinger's shadow copy
-    // of the buffer queue isn't modified when the buffer queue is returning
-    // BufferItem's that weren't actually queued. This can happen in shared
-    // buffer mode.
-    bool queuedBuffer = false;
-    const int32_t layerId = getSequence();
-    LayerRejecter r(mDrawingState, getDrawingState(), recomputeVisibleRegions,
-                    getProducerStickyTransform() != 0, mName,
-                    getTransformToDisplayInverse());
-
-    if (isRemovedFromCurrentState()) {
-        expectedPresentTime = 0;
-    }
-
-    // updateTexImage() below might drop the some buffers at the head of the queue if there is a
-    // buffer behind them which is timely to be presented. However this buffer may not be signaled
-    // yet. The code below makes sure that this wouldn't happen by setting maxFrameNumber to the
-    // last buffer that was signaled.
-    uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
-    {
-        Mutex::Autolock lock(mQueueItemLock);
-        for (size_t i = 0; i < mQueueItems.size(); i++) {
-            bool fenceSignaled =
-                    mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
-            if (!fenceSignaled) {
-                break;
-            }
-            lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber;
-        }
-    }
-    const uint64_t maxFrameNumberToAcquire =
-            std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber);
-
-    bool autoRefresh;
-    status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
-                                                      &queuedBuffer, maxFrameNumberToAcquire);
-    mDrawingState.autoRefresh = autoRefresh;
-    if (updateResult == BufferQueue::PRESENT_LATER) {
-        // Producer doesn't want buffer to be displayed yet.  Signal a
-        // layer update so we check again at the next opportunity.
-        mFlinger->onLayerUpdate();
-        return BAD_VALUE;
-    } else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
-        // If the buffer has been rejected, remove it from the shadow queue
-        // and return early
-        if (queuedBuffer) {
-            Mutex::Autolock lock(mQueueItemLock);
-            if (mQueuedFrames > 0) {
-                mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
-                mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
-                if (mQueueItems[0].surfaceFrame) {
-                    addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
-                }
-                mQueueItems.erase(mQueueItems.begin());
-                mQueuedFrames--;
-            }
-        }
-        return BAD_VALUE;
-    } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
-        // This can occur if something goes wrong when trying to create the
-        // EGLImage for this buffer. If this happens, the buffer has already
-        // been released, so we need to clean up the queue and bug out
-        // early.
-        if (queuedBuffer) {
-            Mutex::Autolock lock(mQueueItemLock);
-            for (auto& [item, surfaceFrame] : mQueueItems) {
-                if (surfaceFrame) {
-                    addSurfaceFrameDroppedForBuffer(surfaceFrame);
-                }
-            }
-            mQueueItems.clear();
-            mQueuedFrames = 0;
-            mFlinger->mTimeStats->onDestroy(layerId);
-            mFlinger->mFrameTracer->onDestroy(layerId);
-        }
-
-        // Once we have hit this state, the shadow queue may no longer
-        // correctly reflect the incoming BufferQueue's contents, so even if
-        // updateTexImage starts working, the only safe course of action is
-        // to continue to ignore updates.
-        mUpdateTexImageFailed = true;
-
-        return BAD_VALUE;
-    }
-
-    bool more_frames_pending = false;
-    if (queuedBuffer) {
-        // Autolock scope
-        auto currentFrameNumber = mConsumer->getFrameNumber();
-
-        Mutex::Autolock lock(mQueueItemLock);
-
-        // Remove any stale buffers that have been dropped during
-        // updateTexImage
-        while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
-            mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
-            if (mQueueItems[0].surfaceFrame) {
-                addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
-            }
-            mQueueItems.erase(mQueueItems.begin());
-            mQueuedFrames--;
-        }
-
-        uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId();
-        mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
-        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
-                                               FrameTracer::FrameEvent::LATCH);
-
-        if (mQueueItems[0].surfaceFrame) {
-            addSurfaceFramePresentedForBuffer(mQueueItems[0].surfaceFrame,
-                                              mQueueItems[0].item.mFenceTime->getSignalTime(),
-                                              latchTime);
-        }
-        mQueueItems.erase(mQueueItems.begin());
-        more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1);
-    }
-
-    // Decrement the queued-frames count.  Signal another event if we
-    // have more frames pending.
-    if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) {
-        mFlinger->onLayerUpdate();
-    }
-
-    return NO_ERROR;
-}
-
-status_t BufferQueueLayer::updateActiveBuffer() {
-    // update the active buffer
-    mPreviousBufferId = getCurrentBufferId();
-    mBufferInfo.mBuffer =
-            mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
-
-    if (mBufferInfo.mBuffer == nullptr) {
-        // this can only happen if the very first buffer was rejected.
-        return BAD_VALUE;
-    }
-    return NO_ERROR;
-}
-
-status_t BufferQueueLayer::updateFrameNumber() {
-    mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mConsumer->getFrameNumber();
-    return NO_ERROR;
-}
-
-void BufferQueueLayer::setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) {
-    mFrameTimelineInfo = frameTimelineInfo;
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for BufferLayerConsumer::ContentsChangedListener
-// -----------------------------------------------------------------------
-
-void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
-    const int32_t layerId = getSequence();
-    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
-                                           systemTime(), FrameTracer::FrameEvent::DEQUEUE);
-}
-
-void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
-    const int32_t layerId = getSequence();
-    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
-                                           systemTime(), FrameTracer::FrameEvent::DETACH);
-}
-
-void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
-    const int32_t layerId = getSequence();
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
-                                           systemTime(), FrameTracer::FrameEvent::CANCEL);
-}
-
-void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
-    const int32_t layerId = getSequence();
-    const uint64_t bufferId = item.mGraphicBuffer->getId();
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
-                                           FrameTracer::FrameEvent::QUEUE);
-    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
-                                       std::make_shared<FenceTime>(item.mFence),
-                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
-
-    ATRACE_CALL();
-    // Add this buffer from our internal queue tracker
-    { // Autolock scope
-        const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-
-        using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
-
-        Mutex::Autolock lock(mQueueItemLock);
-        // Reset the frame number tracker when we receive the first buffer after
-        // a frame number reset
-        if (item.mFrameNumber == 1) {
-            mLastFrameNumberReceived = 0;
-        }
-
-        // Ensure that callbacks are handled in order
-        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
-            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
-            if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", getDebugName());
-                break;
-            }
-        }
-
-        auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName);
-
-        mQueueItems.push_back({item, surfaceFrame});
-        mQueuedFrames++;
-
-        // Wake up any pending callbacks
-        mLastFrameNumberReceived = item.mFrameNumber;
-        mQueueItemCondition.broadcast();
-    }
-
-    mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
-                                             item.mGraphicBuffer->getHeight(), item.mFrameNumber);
-
-    mFlinger->onLayerUpdate();
-    mConsumer->onBufferAvailable(item);
-}
-
-void BufferQueueLayer::onFrameReplaced(const BufferItem& item) {
-    ATRACE_CALL();
-    { // Autolock scope
-        Mutex::Autolock lock(mQueueItemLock);
-
-        // Ensure that callbacks are handled in order
-        while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
-            status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
-            if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", getDebugName());
-                break;
-            }
-        }
-
-        if (!hasFrameUpdate()) {
-            ALOGE("Can't replace a frame on an empty queue");
-            return;
-        }
-
-        auto surfaceFrame = createSurfaceFrameForBuffer(mFrameTimelineInfo, systemTime(), mName);
-        mQueueItems[mQueueItems.size() - 1].item = item;
-        mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
-
-        // Wake up any pending callbacks
-        mLastFrameNumberReceived = item.mFrameNumber;
-        mQueueItemCondition.broadcast();
-    }
-
-    const int32_t layerId = getSequence();
-    const uint64_t bufferId = item.mGraphicBuffer->getId();
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
-                                           FrameTracer::FrameEvent::QUEUE);
-    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
-                                       std::make_shared<FenceTime>(item.mFence),
-                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
-    mConsumer->onBufferAvailable(item);
-}
-
-void BufferQueueLayer::onSidebandStreamChanged() {
-    bool sidebandStreamChanged = false;
-    if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) {
-        // mSidebandStreamChanged was changed to true
-        mFlinger->onLayerUpdate();
-    }
-}
-
-// -----------------------------------------------------------------------
-
-void BufferQueueLayer::onFirstRef() {
-    BufferLayer::onFirstRef();
-
-    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> consumer;
-    mFlinger->getFactory().createBufferQueue(&producer, &consumer, true);
-    mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this);
-    mConsumer =
-            mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
-                                                             mTextureName, this);
-    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-
-    mContentsChangedListener = new ContentsChangedListener(this);
-    mConsumer->setContentsChangedListener(mContentsChangedListener);
-    mConsumer->setName(String8(mName.data(), mName.size()));
-
-    mProducer->setMaxDequeuedBufferCount(2);
-}
-
-status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
-    // never allow a surface larger than what our underlying GL implementation
-    // can handle.
-    if (mFlinger->exceedsMaxRenderTargetSize(w, h)) {
-        ALOGE("dimensions too large %" PRIu32 " x %" PRIu32, w, h);
-        return BAD_VALUE;
-    }
-
-    setDefaultBufferSize(w, h);
-    mConsumer->setDefaultBufferFormat(format);
-    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-
-    return NO_ERROR;
-}
-
-sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const {
-    return mProducer;
-}
-
-uint32_t BufferQueueLayer::getProducerStickyTransform() const {
-    int producerStickyTransform = 0;
-    int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform);
-    if (ret != OK) {
-        ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__,
-              strerror(-ret), ret);
-        return 0;
-    }
-    return static_cast<uint32_t>(producerStickyTransform);
-}
-
-void BufferQueueLayer::gatherBufferInfo() {
-    BufferLayer::gatherBufferInfo();
-
-    mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
-    mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
-    mBufferInfo.mFence = mConsumer->getCurrentFence();
-    mBufferInfo.mTransform = mConsumer->getCurrentTransform();
-    mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace());
-    mBufferInfo.mCrop = mConsumer->getCurrentCrop();
-    mBufferInfo.mScaleMode = mConsumer->getCurrentScalingMode();
-    mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage();
-    mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata();
-    mBufferInfo.mApi = mConsumer->getCurrentApi();
-    mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
-}
-
-sp<Layer> BufferQueueLayer::createClone() {
-    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
-    args.textureName = mTextureName;
-    sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
-    layer->setInitialValuesForClone(this);
-
-    return layer;
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for BufferLayerConsumer::ContentsChangedListener
-// -----------------------------------------------------------------------
-
-void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onFrameAvailable(item);
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onFrameReplaced(item);
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onSidebandStreamChanged();
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onFrameDequeued(bufferId);
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onFrameDetached(bufferId);
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
-    Mutex::Autolock lock(mMutex);
-    if (mBufferQueueLayer != nullptr) {
-        mBufferQueueLayer->onFrameCancelled(bufferId);
-    }
-}
-
-void BufferQueueLayer::ContentsChangedListener::abandon() {
-    Mutex::Autolock lock(mMutex);
-    mBufferQueueLayer = nullptr;
-}
-
-// -----------------------------------------------------------------------
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
deleted file mode 100644
index e1c80d5..0000000
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 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 "BufferLayer.h"
-
-#include <utils/String8.h>
-
-namespace android {
-
-namespace frametimeline {
-class SurfaceFrame;
-}
-
-/*
- * A new BufferQueue and a new BufferLayerConsumer are created when the
- * BufferLayer is first referenced.
- *
- * This also implements onFrameAvailable(), which notifies SurfaceFlinger
- * that new data has arrived.
- */
-class BufferQueueLayer : public BufferLayer {
-public:
-    // Only call while mStateLock is held
-    explicit BufferQueueLayer(const LayerCreationArgs&);
-    ~BufferQueueLayer() override;
-
-    // Implements Layer.
-    const char* getType() const override { return "BufferQueueLayer"; }
-
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
-
-    // If a buffer was replaced this frame, release the former buffer
-    void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
-
-    void setDefaultBufferSize(uint32_t w, uint32_t h) override;
-
-    int32_t getQueuedFrameCount() const override;
-
-    // Returns true if the next buffer should be presented at the expected present time
-    bool isBufferDue(nsecs_t expectedPresentTime) const override;
-
-    // Implements BufferLayer.
-    bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
-
-    status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
-    sp<IGraphicBufferProducer> getProducer() const;
-
-    void setSizeForTest(uint32_t w, uint32_t h) {
-        mDrawingState.active_legacy.w = w;
-        mDrawingState.active_legacy.h = h;
-    }
-
-protected:
-    void gatherBufferInfo() override;
-
-    // -----------------------------------------------------------------------
-    // Interface implementation for BufferLayerConsumer::ContentsChangedListener
-    // -----------------------------------------------------------------------
-    class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
-    public:
-        ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
-              : mBufferQueueLayer(bufferQueueLayer) {}
-        void abandon();
-
-    protected:
-        void onFrameAvailable(const BufferItem& item) override;
-        void onFrameReplaced(const BufferItem& item) override;
-        void onSidebandStreamChanged() override;
-        void onFrameDequeued(const uint64_t bufferId) override;
-        void onFrameDetached(const uint64_t bufferId) override;
-        void onFrameCancelled(const uint64_t bufferId) override;
-
-    private:
-        BufferQueueLayer* mBufferQueueLayer = nullptr;
-        Mutex mMutex;
-    };
-
-private:
-
-    bool latchSidebandStream(bool& recomputeVisibleRegions) override;
-    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
-
-    bool hasFrameUpdate() const override;
-
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                            nsecs_t expectedPresentTime) override;
-
-    status_t updateActiveBuffer() override;
-    status_t updateFrameNumber() override;
-    void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) override;
-
-    sp<Layer> createClone() override;
-
-    void onFirstRef() override;
-
-    void onFrameAvailable(const BufferItem& item);
-    void onFrameReplaced(const BufferItem& item);
-    void onSidebandStreamChanged();
-    void onFrameDequeued(const uint64_t bufferId);
-    void onFrameDetached(const uint64_t bufferId);
-    void onFrameCancelled(const uint64_t bufferId);
-
-    // Temporary - Used only for LEGACY camera mode.
-    uint32_t getProducerStickyTransform() const;
-
-    sp<BufferLayerConsumer> mConsumer;
-    sp<IGraphicBufferProducer> mProducer;
-
-    bool mUpdateTexImageFailed{false};
-
-    uint64_t mPreviousBufferId = 0;
-    uint64_t mPreviousReleasedFrameNumber = 0;
-
-    // Local copy of the queued contents of the incoming BufferQueue
-    mutable Mutex mQueueItemLock;
-    Condition mQueueItemCondition;
-
-    struct BufferData {
-        BufferData(BufferItem item, std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame)
-              : item(item), surfaceFrame(surfaceFrame) {}
-        BufferItem item;
-        std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame;
-    };
-    std::vector<BufferData> mQueueItems;
-    std::atomic<uint64_t> mLastFrameNumberReceived{0};
-
-    // thread-safe
-    std::atomic<int32_t> mQueuedFrames{0};
-
-    sp<ContentsChangedListener> mContentsChangedListener;
-
-    // The last vsync info received on this layer. This will be used when we get
-    // a buffer to correlate the buffer with the vsync id. Can only be accessed
-    // with the SF state lock held.
-    FrameTimelineInfo mFrameTimelineInfo;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
deleted file mode 100644
index e06f3c4..0000000
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ /dev/null
@@ -1,1086 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "BufferStateLayer"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BufferStateLayer.h"
-
-#include <limits>
-
-#include <FrameTimeline/FrameTimeline.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <gui/BufferQueue.h>
-#include <private/gui/SyncFeatures.h>
-#include <renderengine/Image.h>
-#include "TunnelModeEnabledReporter.h"
-
-#include "EffectLayer.h"
-#include "FrameTracer/FrameTracer.h"
-#include "TimeStats/TimeStats.h"
-
-#define EARLY_RELEASE_ENABLED false
-
-namespace android {
-
-using PresentState = frametimeline::SurfaceFrame::PresentState;
-namespace {
-void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
-                               const sp<GraphicBuffer>& buffer, uint64_t framenumber,
-                               const sp<Fence>& releaseFence,
-                               uint32_t currentMaxAcquiredBufferCount) {
-    if (!listener) {
-        return;
-    }
-    listener->onReleaseBuffer({buffer->getId(), framenumber},
-                              releaseFence ? releaseFence : Fence::NO_FENCE,
-                              currentMaxAcquiredBufferCount);
-}
-} // namespace
-
-BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
-      : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
-    mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
-}
-
-BufferStateLayer::~BufferStateLayer() {
-    // The original layer and the clone layer share the same texture and buffer. Therefore, only
-    // one of the layers, in this case the original layer, needs to handle the deletion. The
-    // original layer and the clone should be removed at the same time so there shouldn't be any
-    // issue with the clone layer trying to use the texture.
-    if (mBufferInfo.mBuffer != nullptr) {
-        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
-                                  mBufferInfo.mFence,
-                                  mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                          mOwnerUid));
-    }
-}
-
-// -----------------------------------------------------------------------
-// Interface implementation for Layer
-// -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
-    // If we are displayed on multiple displays in a single composition cycle then we would
-    // need to do careful tracking to enable the use of the mLastClientCompositionFence.
-    //  For example we can only use it if all the displays are client comp, and we need
-    //  to merge all the client comp fences. We could do this, but for now we just
-    // disable the optimization when a layer is composed on multiple displays.
-    if (mClearClientCompositionFenceOnLayerDisplayed) {
-        mLastClientCompositionFence = nullptr;
-    } else {
-        mClearClientCompositionFenceOnLayerDisplayed = true;
-    }
-
-    // The previous release fence notifies the client that SurfaceFlinger is done with the previous
-    // buffer that was presented on this layer. The first transaction that came in this frame that
-    // replaced the previous buffer on this layer needs this release fence, because the fence will
-    // let the client know when that previous buffer is removed from the screen.
-    //
-    // Every other transaction on this layer does not need a release fence because no other
-    // Transactions that were set on this layer this frame are going to have their preceeding buffer
-    // removed from the display this frame.
-    //
-    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
-    // buffer so it doesn't need a previous release fence because the layer still needs the previous
-    // buffer. The second transaction contains a buffer so it needs a previous release fence because
-    // the previous buffer will be released this frame. The third transaction also contains a
-    // buffer. It replaces the buffer in the second transaction. The buffer in the second
-    // transaction will now no longer be presented so it is released immediately and the third
-    // transaction doesn't need a previous release fence.
-    sp<CallbackHandle> ch;
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            ch = handle;
-            break;
-        }
-    }
-
-    // Prevent tracing the same release multiple times.
-    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
-        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
-    }
-
-    if (ch != nullptr) {
-        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-        ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
-        ch->name = mName;
-    }
-}
-
-void BufferStateLayer::onSurfaceFrameCreated(
-        const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
-    while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
-        // Too many SurfaceFrames pending classification. The front of the deque is probably not
-        // tracked by FrameTimeline and will never be presented. This will only result in a memory
-        // leak.
-        ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
-              mName.c_str());
-        std::string miniDump = mPendingJankClassifications.front()->miniDump();
-        ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
-        mPendingJankClassifications.pop_front();
-    }
-    mPendingJankClassifications.emplace_back(surfaceFrame);
-}
-
-void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->transformHint = mTransformHint;
-        handle->dequeueReadyTime = dequeueReadyTime;
-        handle->currentMaxAcquiredBufferCount =
-                mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
-    }
-
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
-            break;
-        }
-    }
-
-    std::vector<JankData> jankData;
-    jankData.reserve(mPendingJankClassifications.size());
-    while (!mPendingJankClassifications.empty()
-            && mPendingJankClassifications.front()->getJankType()) {
-        std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
-                mPendingJankClassifications.front();
-        mPendingJankClassifications.pop_front();
-        jankData.emplace_back(
-                JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
-    }
-
-    mFlinger->getTransactionCallbackInvoker().addCallbackHandles(
-            mDrawingState.callbackHandles, jankData);
-
-    sp<Fence> releaseFence = Fence::NO_FENCE;
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->releasePreviousBuffer &&
-            mDrawingState.releaseBufferEndpoint == handle->listener) {
-            releaseFence =
-                    handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE;
-            break;
-        }
-    }
-
-    mDrawingState.callbackHandles = {};
-}
-
-void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
-                                                 const CompositorTiming& compositorTiming) {
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->gpuCompositionDoneFence = glDoneFence;
-        handle->compositorTiming = compositorTiming;
-    }
-}
-
-bool BufferStateLayer::willPresentCurrentTransaction() const {
-    // Returns true if the most recent Transaction applied to CurrentState will be presented.
-    return (getSidebandStreamChanged() || getAutoRefresh() ||
-            (mDrawingState.modified &&
-             (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
-}
-
-Rect BufferStateLayer::getCrop(const Layer::State& s) const {
-    return s.crop;
-}
-
-bool BufferStateLayer::setTransform(uint32_t transform) {
-    if (mDrawingState.bufferTransform == transform) return false;
-    mDrawingState.bufferTransform = transform;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
-    if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
-    mDrawingState.sequence++;
-    mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setCrop(const Rect& crop) {
-    if (mDrawingState.crop == crop) return false;
-    mDrawingState.sequence++;
-    mDrawingState.crop = crop;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) {
-    if (mDrawingState.bufferCrop == bufferCrop) return false;
-
-    mDrawingState.sequence++;
-    mDrawingState.bufferCrop = bufferCrop;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) {
-    if (mDrawingState.destinationFrame == destinationFrame) return false;
-
-    mDrawingState.sequence++;
-    mDrawingState.destinationFrame = destinationFrame;
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-static bool assignTransform(ui::Transform* dst, ui::Transform& from) {
-    if (*dst == from) {
-        return false;
-    }
-    *dst = from;
-    return true;
-}
-
-// Translate destination frame into scale and position. If a destination frame is not set, use the
-// provided scale and position
-bool BufferStateLayer::updateGeometry() {
-    if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
-        mDrawingState.destinationFrame.isEmpty()) {
-        // If destination frame is not set, use the requested transform set via
-        // BufferStateLayer::setPosition and BufferStateLayer::setMatrix.
-        return assignTransform(&mDrawingState.transform, mRequestedTransform);
-    }
-
-    Rect destRect = mDrawingState.destinationFrame;
-    int32_t destW = destRect.width();
-    int32_t destH = destRect.height();
-    if (destRect.left < 0) {
-        destRect.left = 0;
-        destRect.right = destW;
-    }
-    if (destRect.top < 0) {
-        destRect.top = 0;
-        destRect.bottom = destH;
-    }
-
-    if (!mDrawingState.buffer) {
-        ui::Transform t;
-        t.set(destRect.left, destRect.top);
-        return assignTransform(&mDrawingState.transform, t);
-    }
-
-    uint32_t bufferWidth = mDrawingState.buffer->getWidth();
-    uint32_t bufferHeight = mDrawingState.buffer->getHeight();
-    // Undo any transformations on the buffer.
-    if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-    uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-    if (mDrawingState.transformToDisplayInverse) {
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufferWidth, bufferHeight);
-        }
-    }
-
-    float sx = destW / static_cast<float>(bufferWidth);
-    float sy = destH / static_cast<float>(bufferHeight);
-    ui::Transform t;
-    t.set(sx, 0, 0, sy);
-    t.set(destRect.left, destRect.top);
-    return assignTransform(&mDrawingState.transform, t);
-}
-
-bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix) {
-    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
-        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
-        return false;
-    }
-
-    ui::Transform t;
-    t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    return true;
-}
-
-bool BufferStateLayer::setPosition(float x, float y) {
-    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
-        return false;
-    }
-
-    mRequestedTransform.set(x, y);
-
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    return true;
-}
-
-bool BufferStateLayer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
-                                 const BufferData& bufferData, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime, bool isAutoTimestamp,
-                                 std::optional<nsecs_t> dequeueTime,
-                                 const FrameTimelineInfo& info) {
-    ATRACE_CALL();
-
-    if (!buffer) {
-        return false;
-    }
-
-    const bool frameNumberChanged =
-            bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
-    const uint64_t frameNumber =
-            frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
-
-    if (mDrawingState.buffer) {
-        mReleasePreviousBuffer = true;
-        if (!mBufferInfo.mBuffer ||
-            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
-             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
-            // If mDrawingState has a buffer, and we are about to update again
-            // before swapping to drawing state, then the first buffer will be
-            // dropped and we should decrement the pending buffer count and
-            // call any release buffer callbacks if set.
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mDrawingState.acquireFence,
-                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                              mOwnerUid));
-            decrementPendingBufferCount();
-            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
-                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
-              addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
-              mDrawingState.bufferSurfaceFrameTX.reset();
-            }
-        } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
-            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
-                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
-                                      mLastClientCompositionFence,
-                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
-                                              mOwnerUid));
-            mLastClientCompositionFence = nullptr;
-        }
-    }
-
-    mDrawingState.frameNumber = frameNumber;
-    mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
-    mDrawingState.buffer = std::move(buffer);
-    mDrawingState.clientCacheId = bufferData.cachedBuffer;
-
-    mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
-            ? bufferData.acquireFence
-            : Fence::NO_FENCE;
-    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
-    if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
-        // We latched this buffer unsiganled, so we need to pass the acquire fence
-        // on the callback instead of just the acquire time, since it's unknown at
-        // this point.
-        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence;
-    } else {
-        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
-    }
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    const int32_t layerId = getSequence();
-    mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
-                                      mOwnerUid, postTime, getGameMode());
-    mDrawingState.desiredPresentTime = desiredPresentTime;
-    mDrawingState.isAutoTimestamp = isAutoTimestamp;
-
-    const nsecs_t presentTime = [&] {
-        if (!isAutoTimestamp) return desiredPresentTime;
-
-        const auto prediction =
-                mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(info.vsyncId);
-        if (prediction.has_value()) return prediction->presentTime;
-
-        return static_cast<nsecs_t>(0);
-    }();
-
-    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-    mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
-
-    setFrameTimelineVsyncForBufferTransaction(info, postTime);
-
-    if (dequeueTime && *dequeueTime != 0) {
-        const uint64_t bufferId = mDrawingState.buffer->getId();
-        mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
-        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
-                                               FrameTracer::FrameEvent::DEQUEUE);
-        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
-                                               FrameTracer::FrameEvent::QUEUE);
-    }
-
-    mDrawingState.width = mDrawingState.buffer->getWidth();
-    mDrawingState.height = mDrawingState.buffer->getHeight();
-    mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
-    return true;
-}
-
-bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mDrawingState.dataspace == dataspace) return false;
-    mDrawingState.dataspace = dataspace;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
-    if (mDrawingState.hdrMetadata == hdrMetadata) return false;
-    mDrawingState.hdrMetadata = hdrMetadata;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) {
-    if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
-    mDrawingState.surfaceDamageRegion = surfaceDamage;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setApi(int32_t api) {
-    if (mDrawingState.api == api) return false;
-    mDrawingState.api = api;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
-    if (mDrawingState.sidebandStream == sidebandStream) return false;
-
-    if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
-        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
-    } else if (sidebandStream != nullptr) {
-        mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
-    }
-
-    mDrawingState.sidebandStream = sidebandStream;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    if (!mSidebandStreamChanged.exchange(true)) {
-        // mSidebandStreamChanged was false
-        mFlinger->onLayerUpdate();
-    }
-    return true;
-}
-
-bool BufferStateLayer::setTransactionCompletedListeners(
-        const std::vector<sp<CallbackHandle>>& handles) {
-    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
-    if (handles.empty()) {
-        mReleasePreviousBuffer = false;
-        return false;
-    }
-
-    const bool willPresent = willPresentCurrentTransaction();
-
-    for (const auto& handle : handles) {
-        // If this transaction set a buffer on this layer, release its previous buffer
-        handle->releasePreviousBuffer = mReleasePreviousBuffer;
-
-        // If this layer will be presented in this frame
-        if (willPresent) {
-            // If this transaction set an acquire fence on this layer, set its acquire time
-            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
-            handle->frameNumber = mDrawingState.frameNumber;
-
-            // Store so latched time and release fence can be set
-            mDrawingState.callbackHandles.push_back(handle);
-
-        } else { // If this layer will NOT need to be relatched and presented this frame
-            // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
-        }
-    }
-
-    mReleasePreviousBuffer = false;
-    mCallbackHandleAcquireTimeOrFence = -1;
-
-    return willPresent;
-}
-
-bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
-    mDrawingState.sequence++;
-    mDrawingState.transparentRegionHint = transparent;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-Rect BufferStateLayer::getBufferSize(const State& /*s*/) const {
-    // for buffer state layers we use the display frame size as the buffer size.
-
-    if (mBufferInfo.mBuffer == nullptr) {
-        return Rect::INVALID_RECT;
-    }
-
-    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
-    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
-
-    // Undo any transformations on the buffer and return the result.
-    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
-}
-
-FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
-    if (mBufferInfo.mBuffer == nullptr) {
-        return parentBounds;
-    }
-
-    return getBufferSize(getDrawingState()).toFloatRect();
-}
-
-// -----------------------------------------------------------------------
-
-// -----------------------------------------------------------------------
-// Interface implementation for BufferLayer
-// -----------------------------------------------------------------------
-bool BufferStateLayer::fenceHasSignaled() const {
-    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-        return true;
-    }
-
-    const bool fenceSignaled =
-            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
-    if (!fenceSignaled) {
-        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
-                                                    TimeStats::LatchSkipReason::LateAcquire);
-    }
-
-    return fenceSignaled;
-}
-
-bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
-    if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
-        return true;
-    }
-
-    return mDrawingState.isAutoTimestamp || mDrawingState.desiredPresentTime <= expectedPresentTime;
-}
-
-bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
-    for (const auto& handle : mDrawingState.callbackHandles) {
-        handle->refreshStartTime = refreshStartTime;
-    }
-    return BufferLayer::onPreComposition(refreshStartTime);
-}
-
-void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
-    mDrawingState.autoRefresh = autoRefresh;
-}
-
-bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
-    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
-    editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
-
-    if (mSidebandStreamChanged.exchange(false)) {
-        const State& s(getDrawingState());
-        // mSidebandStreamChanged was true
-        mSidebandStream = s.sidebandStream;
-        editCompositionState()->sidebandStream = mSidebandStream;
-        if (mSidebandStream != nullptr) {
-            setTransactionFlags(eTransactionNeeded);
-            mFlinger->setTransactionFlags(eTraversalNeeded);
-        }
-        recomputeVisibleRegions = true;
-
-        return true;
-    }
-    return false;
-}
-
-bool BufferStateLayer::hasFrameUpdate() const {
-    const State& c(getDrawingState());
-    return (mDrawingStateModified || mDrawingState.modified) && (c.buffer != nullptr || c.bgColorLayer != nullptr);
-}
-
-status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
-                                          nsecs_t /*expectedPresentTime*/) {
-    const State& s(getDrawingState());
-
-    if (!s.buffer) {
-        if (s.bgColorLayer) {
-            for (auto& handle : mDrawingState.callbackHandles) {
-                handle->latchTime = latchTime;
-            }
-        }
-        return NO_ERROR;
-    }
-
-    for (auto& handle : mDrawingState.callbackHandles) {
-        if (handle->frameNumber == mDrawingState.frameNumber) {
-            handle->latchTime = latchTime;
-        }
-    }
-
-    const int32_t layerId = getSequence();
-    const uint64_t bufferId = mDrawingState.buffer->getId();
-    const uint64_t frameNumber = mDrawingState.frameNumber;
-    const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
-    mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
-    mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
-
-    mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
-                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
-    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
-                                           FrameTracer::FrameEvent::LATCH);
-
-    auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
-    if (bufferSurfaceFrame != nullptr &&
-        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
-        // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
-        // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
-        // are processing the next state.
-        addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
-                                          mDrawingState.acquireFenceTime->getSignalTime(),
-                                          latchTime);
-        mDrawingState.bufferSurfaceFrameTX.reset();
-    }
-
-    std::deque<sp<CallbackHandle>> remainingHandles;
-    mFlinger->getTransactionCallbackInvoker()
-            .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
-    mDrawingState.callbackHandles = remainingHandles;
-
-    mDrawingStateModified = false;
-
-    return NO_ERROR;
-}
-
-status_t BufferStateLayer::updateActiveBuffer() {
-    const State& s(getDrawingState());
-
-    if (s.buffer == nullptr) {
-        return BAD_VALUE;
-    }
-
-    if (!mBufferInfo.mBuffer || !s.buffer->hasSameBuffer(*mBufferInfo.mBuffer)) {
-        decrementPendingBufferCount();
-    }
-
-    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
-    mBufferInfo.mBuffer = s.buffer;
-    mBufferInfo.mFence = s.acquireFence;
-    mBufferInfo.mFrameNumber = s.frameNumber;
-
-    return NO_ERROR;
-}
-
-status_t BufferStateLayer::updateFrameNumber() {
-    // TODO(marissaw): support frame history events
-    mPreviousFrameNumber = mCurrentFrameNumber;
-    mCurrentFrameNumber = mDrawingState.frameNumber;
-    return NO_ERROR;
-}
-
-void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
-    std::lock_guard lock(mMutex);
-    if (!clientCacheId.isValid()) {
-        ALOGE("invalid process, failed to erase buffer");
-        return;
-    }
-    eraseBufferLocked(clientCacheId);
-}
-
-int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
-    std::lock_guard<std::mutex> lock(mMutex);
-    auto itr = mCachedBuffers.find(clientCacheId);
-    if (itr == mCachedBuffers.end()) {
-        return addCachedBuffer(clientCacheId);
-    }
-    auto& [hwcCacheSlot, counter] = itr->second;
-    counter = mCounter++;
-    return hwcCacheSlot;
-}
-
-int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
-        REQUIRES(mMutex) {
-    if (!clientCacheId.isValid()) {
-        ALOGE("invalid process, returning invalid slot");
-        return BufferQueue::INVALID_BUFFER_SLOT;
-    }
-
-    ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
-
-    int hwcCacheSlot = getFreeHwcCacheSlot();
-    mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
-    return hwcCacheSlot;
-}
-
-int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
-    if (mFreeHwcCacheSlots.empty()) {
-        evictLeastRecentlyUsed();
-    }
-
-    int hwcCacheSlot = mFreeHwcCacheSlots.top();
-    mFreeHwcCacheSlots.pop();
-    return hwcCacheSlot;
-}
-
-void BufferStateLayer::HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
-    uint64_t minCounter = UINT_MAX;
-    client_cache_t minClientCacheId = {};
-    for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
-        const auto& [hwcCacheSlot, counter] = slotCounter;
-        if (counter < minCounter) {
-            minCounter = counter;
-            minClientCacheId = clientCacheId;
-        }
-    }
-    eraseBufferLocked(minClientCacheId);
-
-    ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId, this);
-}
-
-void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId)
-        REQUIRES(mMutex) {
-    auto itr = mCachedBuffers.find(clientCacheId);
-    if (itr == mCachedBuffers.end()) {
-        return;
-    }
-    auto& [hwcCacheSlot, counter] = itr->second;
-
-    // TODO send to hwc cache and resources
-
-    mFreeHwcCacheSlots.push(hwcCacheSlot);
-    mCachedBuffers.erase(clientCacheId);
-}
-
-void BufferStateLayer::gatherBufferInfo() {
-    BufferLayer::gatherBufferInfo();
-
-    const State& s(getDrawingState());
-    mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
-    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
-    mBufferInfo.mFence = s.acquireFence;
-    mBufferInfo.mTransform = s.bufferTransform;
-    auto lastDataspace = mBufferInfo.mDataspace;
-    mBufferInfo.mDataspace = translateDataspace(s.dataspace);
-    if (lastDataspace != mBufferInfo.mDataspace) {
-        mFlinger->mSomeDataspaceChanged = true;
-    }
-    mBufferInfo.mCrop = computeBufferCrop(s);
-    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
-    mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
-    mBufferInfo.mHdrMetadata = s.hdrMetadata;
-    mBufferInfo.mApi = s.api;
-    mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
-    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
-}
-
-uint32_t BufferStateLayer::getEffectiveScalingMode() const {
-   return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
-}
-
-Rect BufferStateLayer::computeBufferCrop(const State& s) {
-    if (s.buffer && !s.bufferCrop.isEmpty()) {
-        Rect bufferCrop;
-        s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
-        return bufferCrop;
-    } else if (s.buffer) {
-        return s.buffer->getBounds();
-    } else {
-        return s.bufferCrop;
-    }
-}
-
-sp<Layer> BufferStateLayer::createClone() {
-    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
-    args.textureName = mTextureName;
-    sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
-    layer->mHwcSlotGenerator = mHwcSlotGenerator;
-    layer->setInitialValuesForClone(this);
-    return layer;
-}
-
-bool BufferStateLayer::bufferNeedsFiltering() const {
-    const State& s(getDrawingState());
-    if (!s.buffer) {
-        return false;
-    }
-
-    int32_t bufferWidth = static_cast<int32_t>(s.buffer->getWidth());
-    int32_t bufferHeight = static_cast<int32_t>(s.buffer->getHeight());
-
-    // Undo any transformations on the buffer and return the result.
-    if (s.bufferTransform & ui::Transform::ROT_90) {
-        std::swap(bufferWidth, bufferHeight);
-    }
-
-    if (s.transformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufferWidth, bufferHeight);
-        }
-    }
-
-    const Rect layerSize{getBounds()};
-    return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
-}
-
-void BufferStateLayer::decrementPendingBufferCount() {
-    int32_t pendingBuffers = --mPendingBufferTransactions;
-    tracePendingBufferCount(pendingBuffers);
-}
-
-void BufferStateLayer::tracePendingBufferCount(int32_t pendingBuffers) {
-    ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
-}
-
-
-/*
- * We don't want to send the layer's transform to input, but rather the
- * parent's transform. This is because BufferStateLayer's transform is
- * information about how the buffer is placed on screen. The parent's
- * transform makes more sense to send since it's information about how the
- * layer is placed on screen. This transform is used by input to determine
- * how to go from screen space back to window space.
- */
-ui::Transform BufferStateLayer::getInputTransform() const {
-    sp<Layer> parent = mDrawingParent.promote();
-    if (parent == nullptr) {
-        return ui::Transform();
-    }
-
-    return parent->getTransform();
-}
-
-/**
- * Similar to getInputTransform, we need to update the bounds to include the transform.
- * This is because bounds for BSL doesn't include buffer transform, where the input assumes
- * that's already included.
- */
-Rect BufferStateLayer::getInputBounds() const {
-    Rect bufferBounds = getCroppedBufferSize(getDrawingState());
-    if (mDrawingState.transform.getType() == ui::Transform::IDENTITY || !bufferBounds.isValid()) {
-        return bufferBounds;
-    }
-    return mDrawingState.transform.transform(bufferBounds);
-}
-
-bool BufferStateLayer::simpleBufferUpdate(const layer_state_t& s) const {
-    const uint64_t requiredFlags = layer_state_t::eBufferChanged;
-
-    const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
-            layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
-            layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
-            layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
-            layer_state_t::eReparent;
-
-    const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
-            layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
-            layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
-            layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
-            layer_state_t::eInputInfoChanged;
-
-    if ((s.what & requiredFlags) != requiredFlags) {
-        ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
-              (s.what | requiredFlags) & ~s.what);
-        return false;
-    }
-
-    if (s.what & deniedFlags) {
-        ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
-        return false;
-    }
-
-    if (s.what & allowedFlags) {
-        ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
-    }
-
-    if (s.what & layer_state_t::ePositionChanged) {
-        if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
-            ALOGV("%s: false [ePositionChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eAlphaChanged) {
-        if (mDrawingState.color.a != s.alpha) {
-            ALOGV("%s: false [eAlphaChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eColorTransformChanged) {
-        if (mDrawingState.colorTransform != s.colorTransform) {
-            ALOGV("%s: false [eColorTransformChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBackgroundColorChanged) {
-        if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) {
-            ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eMatrixChanged) {
-        if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
-            mRequestedTransform.dtdy() != s.matrix.dtdy ||
-            mRequestedTransform.dtdx() != s.matrix.dtdx ||
-            mRequestedTransform.dsdy() != s.matrix.dsdy) {
-            ALOGV("%s: false [eMatrixChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eCornerRadiusChanged) {
-        if (mDrawingState.cornerRadius != s.cornerRadius) {
-            ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
-        if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
-            ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTransformChanged) {
-        if (mDrawingState.bufferTransform != s.transform) {
-            ALOGV("%s: false [eTransformChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
-        if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
-            ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eCropChanged) {
-        if (mDrawingState.crop != s.crop) {
-            ALOGV("%s: false [eCropChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDataspaceChanged) {
-        if (mDrawingState.dataspace != s.dataspace) {
-            ALOGV("%s: false [eDataspaceChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eHdrMetadataChanged) {
-        if (mDrawingState.hdrMetadata != s.hdrMetadata) {
-            ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eSidebandStreamChanged) {
-        if (mDrawingState.sidebandStream != s.sidebandStream) {
-            ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
-        if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
-            ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eShadowRadiusChanged) {
-        if (mDrawingState.shadowRadius != s.shadowRadius) {
-            ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eFixedTransformHintChanged) {
-        if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
-            ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eTrustedOverlayChanged) {
-        if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
-            ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eStretchChanged) {
-        StretchEffect temp = s.stretchEffect;
-        temp.sanitize();
-        if (mDrawingState.stretchEffect != temp) {
-            ALOGV("%s: false [eStretchChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eBufferCropChanged) {
-        if (mDrawingState.bufferCrop != s.bufferCrop) {
-            ALOGV("%s: false [eBufferCropChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDestinationFrameChanged) {
-        if (mDrawingState.destinationFrame != s.destinationFrame) {
-            ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    if (s.what & layer_state_t::eDimmingEnabledChanged) {
-        if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
-            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
-            return false;
-        }
-    }
-
-    ALOGV("%s: true", __func__);
-    return true;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
deleted file mode 100644
index 3f0dbe4..0000000
--- a/services/surfaceflinger/BufferStateLayer.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 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 "BufferLayer.h"
-#include "Layer.h"
-
-#include <renderengine/Image.h>
-#include <renderengine/RenderEngine.h>
-#include <system/window.h>
-#include <utils/String8.h>
-
-#include <stack>
-
-namespace android {
-
-class SlotGenerationTest;
-
-class BufferStateLayer : public BufferLayer {
-public:
-    explicit BufferStateLayer(const LayerCreationArgs&);
-
-    ~BufferStateLayer() override;
-
-    // Implements Layer.
-    const char* getType() const override { return "BufferStateLayer"; }
-
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
-
-    void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
-
-    void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
-                                   const CompositorTiming& compositorTiming) override;
-
-    bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; }
-
-    Region getActiveTransparentRegion(const Layer::State& s) const override {
-        return s.transparentRegionHint;
-    }
-    Rect getCrop(const Layer::State& s) const;
-
-    bool setTransform(uint32_t transform) override;
-    bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
-    bool setCrop(const Rect& crop) override;
-    bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
-                   const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
-                   bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
-                   const FrameTimelineInfo& info) override;
-    bool setDataspace(ui::Dataspace dataspace) override;
-    bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
-    bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
-    bool setApi(int32_t api) override;
-    bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
-    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
-    bool setPosition(float /*x*/, float /*y*/) override;
-    bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/);
-
-    // Override to ignore legacy layer state properties that are not used by BufferStateLayer
-    bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
-    bool setTransparentRegionHint(const Region& transparent) override;
-
-    Rect getBufferSize(const State& s) const override;
-    FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-    void setAutoRefresh(bool autoRefresh) override;
-
-    bool setBufferCrop(const Rect& bufferCrop) override;
-    bool setDestinationFrame(const Rect& destinationFrame) override;
-    bool updateGeometry() override;
-
-    // -----------------------------------------------------------------------
-
-    // -----------------------------------------------------------------------
-    // Interface implementation for BufferLayer
-    // -----------------------------------------------------------------------
-    bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
-    bool onPreComposition(nsecs_t refreshStartTime) override;
-    uint32_t getEffectiveScalingMode() const override;
-
-    // See mPendingBufferTransactions
-    void decrementPendingBufferCount();
-    std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
-    std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
-
-    bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
-
-protected:
-    void gatherBufferInfo() override;
-    void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
-    ui::Transform getInputTransform() const override;
-    Rect getInputBounds() const override;
-
-private:
-    friend class SlotGenerationTest;
-    friend class TransactionFrameTracerTest;
-    friend class TransactionSurfaceFrameTest;
-
-    inline void tracePendingBufferCount(int32_t pendingBuffers);
-
-    bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
-                                 nsecs_t requestedPresentTime);
-
-    bool latchSidebandStream(bool& recomputeVisibleRegions) override;
-
-    bool hasFrameUpdate() const override;
-
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
-                            nsecs_t expectedPresentTime) override;
-
-    status_t updateActiveBuffer() override;
-    status_t updateFrameNumber() override;
-
-    sp<Layer> createClone() override;
-
-    // Crop that applies to the buffer
-    Rect computeBufferCrop(const State& s);
-
-    bool willPresentCurrentTransaction() const;
-
-    bool bufferNeedsFiltering() const override;
-
-    bool simpleBufferUpdate(const layer_state_t& s) const override;
-
-    ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
-    uint64_t mPreviousReleasedFrameNumber = 0;
-
-    uint64_t mPreviousBarrierFrameNumber = 0;
-
-    bool mReleasePreviousBuffer = false;
-
-    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
-    // time.
-    std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1;
-
-    std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
-    // An upper bound on the number of SurfaceFrames in the pending classifications deque.
-    static constexpr int kPendingClassificationMaxSurfaceFrames = 25;
-
-    const std::string mBlastTransactionName{"BufferTX - " + mName};
-    // This integer is incremented everytime a buffer arrives at the server for this layer,
-    // and decremented when a buffer is dropped or latched. When changed the integer is exported
-    // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
-    // possible to see when a buffer arrived at the server, and in which frame it latched.
-    //
-    // You can understand the trace this way:
-    //     - 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 or doTransaction, a buffer was dropped
-    std::atomic<int32_t> mPendingBufferTransactions{0};
-
-    // Contains requested position and matrix updates. This will be applied if the client does
-    // not specify a destination frame.
-    ui::Transform mRequestedTransform;
-
-    // TODO(marissaw): support sticky transform for LEGACY camera mode
-
-    class HwcSlotGenerator : public ClientCache::ErasedRecipient {
-    public:
-        HwcSlotGenerator() {
-            for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-                mFreeHwcCacheSlots.push(i);
-            }
-        }
-
-        void bufferErased(const client_cache_t& clientCacheId);
-
-        int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
-    private:
-        friend class SlotGenerationTest;
-        int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-        int getFreeHwcCacheSlot() REQUIRES(mMutex);
-        void evictLeastRecentlyUsed() REQUIRES(mMutex);
-        void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
-        struct CachedBufferHash {
-            std::size_t operator()(const client_cache_t& clientCacheId) const {
-                return std::hash<uint64_t>{}(clientCacheId.id);
-            }
-        };
-
-        std::mutex mMutex;
-
-        std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
-                           CachedBufferHash>
-                mCachedBuffers GUARDED_BY(mMutex);
-        std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
-        // The cache increments this counter value when a slot is updated or used.
-        // Used to track the least recently-used buffer
-        uint64_t mCounter = 0;
-    };
-
-    sp<HwcSlotGenerator> mHwcSlotGenerator;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 6d7b732..bdbc79b 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -21,12 +21,18 @@
 
 #include <private/android_filesystem_config.h>
 
+#include <gui/AidlStatusUtil.h>
+
 #include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
 
+using gui::aidl_utils::binderStatusFromStatusT;
+
 // ---------------------------------------------------------------------------
 
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
@@ -42,82 +48,73 @@
     return NO_ERROR;
 }
 
-void Client::attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer)
-{
-    Mutex::Autolock _l(mLock);
-    mLayers.add(handle, layer);
-}
-
-void Client::detachLayer(const Layer* layer)
-{
-    Mutex::Autolock _l(mLock);
-    // we do a linear search here, because this doesn't happen often
-    const size_t count = mLayers.size();
-    for (size_t i=0 ; i<count ; i++) {
-        if (mLayers.valueAt(i) == layer) {
-            mLayers.removeItemsAt(i, 1);
-            break;
-        }
-    }
-}
-sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const
-{
-    Mutex::Autolock _l(mLock);
-    sp<Layer> lbc;
-    wp<Layer> layer(mLayers.valueFor(handle));
-    if (layer != 0) {
-        lbc = layer.promote();
-        ALOGE_IF(lbc==0, "getLayerUser(name=%p) is dead", handle.get());
-    }
-    return lbc;
-}
-
-status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */,
-                               PixelFormat /* format */, uint32_t flags,
-                               const sp<IBinder>& parentHandle, LayerMetadata metadata,
-                               sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */,
-                               int32_t* outLayerId, uint32_t* outTransformHint) {
+binder::Status Client::createSurface(const std::string& name, int32_t flags,
+                                     const sp<IBinder>& parent, const gui::LayerMetadata& metadata,
+                                     gui::CreateSurfaceResult* outResult) {
     // We rely on createLayer to check permissions.
-    LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata));
-    return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr,
-                                 outTransformHint);
+    sp<IBinder> handle;
+    LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), name.c_str(),
+                           static_cast<uint32_t>(flags), std::move(metadata));
+    args.parentHandle = parent;
+    const status_t status = mFlinger->createLayer(args, *outResult);
+    return binderStatusFromStatusT(status);
 }
 
-status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */,
-                                         uint32_t /* h */, PixelFormat /* format */,
-                                         uint32_t /* flags */,
-                                         const sp<IGraphicBufferProducer>& /* parent */,
-                                         LayerMetadata /* metadata */, sp<IBinder>* /* handle */,
-                                         sp<IGraphicBufferProducer>* /* gbp */,
-                                         int32_t* /* outLayerId */,
-                                         uint32_t* /* outTransformHint */) {
-    // This api does not make sense with blast since SF no longer tracks IGBP. This api should be
-    // removed.
-    return BAD_VALUE;
-}
-
-status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                               int32_t* outLayerId) {
-    LayerCreationArgs args(mFlinger.get(), this, "MirrorRoot", 0 /* flags */, LayerMetadata());
-    return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
-}
-
-status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
-    sp<Layer> layer = getLayerUser(handle);
+binder::Status Client::clearLayerFrameStats(const sp<IBinder>& handle) {
+    status_t status;
+    sp<Layer> layer = LayerHandle::getLayer(handle);
     if (layer == nullptr) {
-        return NAME_NOT_FOUND;
+        status = NAME_NOT_FOUND;
+    } else {
+        layer->clearFrameStats();
+        status = NO_ERROR;
     }
-    layer->clearFrameStats();
-    return NO_ERROR;
+    return binderStatusFromStatusT(status);
 }
 
-status_t Client::getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const {
-    sp<Layer> layer = getLayerUser(handle);
+binder::Status Client::getLayerFrameStats(const sp<IBinder>& handle, gui::FrameStats* outStats) {
+    status_t status;
+    sp<Layer> layer = LayerHandle::getLayer(handle);
     if (layer == nullptr) {
-        return NAME_NOT_FOUND;
+        status = NAME_NOT_FOUND;
+    } else {
+        FrameStats stats;
+        layer->getFrameStats(&stats);
+        outStats->refreshPeriodNano = stats.refreshPeriodNano;
+        outStats->desiredPresentTimesNano.reserve(stats.desiredPresentTimesNano.size());
+        for (const auto& t : stats.desiredPresentTimesNano) {
+            outStats->desiredPresentTimesNano.push_back(t);
+        }
+        outStats->actualPresentTimesNano.reserve(stats.actualPresentTimesNano.size());
+        for (const auto& t : stats.actualPresentTimesNano) {
+            outStats->actualPresentTimesNano.push_back(t);
+        }
+        outStats->frameReadyTimesNano.reserve(stats.frameReadyTimesNano.size());
+        for (const auto& t : stats.frameReadyTimesNano) {
+            outStats->frameReadyTimesNano.push_back(t);
+        }
+        status = NO_ERROR;
     }
-    layer->getFrameStats(outStats);
-    return NO_ERROR;
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle,
+                                     gui::CreateSurfaceResult* outResult) {
+    sp<IBinder> handle;
+    LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this), "MirrorRoot",
+                           0 /* flags */, gui::LayerMetadata());
+    status_t status = mFlinger->mirrorLayer(args, mirrorFromHandle, *outResult);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status Client::mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) {
+    sp<IBinder> handle;
+    LayerCreationArgs args(mFlinger.get(), sp<Client>::fromExisting(this),
+                           "MirrorRoot-" + std::to_string(displayId), 0 /* flags */,
+                           gui::LayerMetadata());
+    std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
+    status_t status = mFlinger->mirrorDisplay(*id, args, *outResult);
+    return binderStatusFromStatusT(status);
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 15cd763..af410ea 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -24,55 +24,40 @@
 #include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
 
-#include <gui/ISurfaceComposerClient.h>
+#include <android/gui/BnSurfaceComposerClient.h>
 
 namespace android {
 
 class Layer;
 class SurfaceFlinger;
 
-class Client : public BnSurfaceComposerClient
-{
+class Client : public gui::BnSurfaceComposerClient {
 public:
     explicit Client(const sp<SurfaceFlinger>& flinger);
     ~Client() = default;
 
     status_t initCheck() const;
 
-    // protected by SurfaceFlinger::mStateLock
-    void attachLayer(const sp<IBinder>& handle, const sp<Layer>& layer);
-    void detachLayer(const Layer* layer);
-
-    sp<Layer> getLayerUser(const sp<IBinder>& handle) const;
-
 private:
     // ISurfaceComposerClient interface
-    virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
-                                   uint32_t flags, const sp<IBinder>& parent,
-                                   LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
-                                   uint32_t* outTransformHint = nullptr);
 
-    virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
-                                             PixelFormat format, uint32_t flags,
-                                             const sp<IGraphicBufferProducer>& parent,
-                                             LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
-                                             uint32_t* outTransformHint = nullptr);
+    binder::Status createSurface(const std::string& name, int32_t flags, const sp<IBinder>& parent,
+                                 const gui::LayerMetadata& metadata,
+                                 gui::CreateSurfaceResult* outResult) override;
 
-    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle,
-                           int32_t* outLayerId);
+    binder::Status clearLayerFrameStats(const sp<IBinder>& handle) override;
 
-    virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
+    binder::Status getLayerFrameStats(const sp<IBinder>& handle,
+                                      gui::FrameStats* outStats) override;
 
-    virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const;
+    binder::Status mirrorSurface(const sp<IBinder>& mirrorFromHandle,
+                                 gui::CreateSurfaceResult* outResult) override;
+
+    binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
 
     // constant
     sp<SurfaceFlinger> mFlinger;
 
-    // protected by mLock
-    DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayers;
-
     // thread-safe
     mutable Mutex mLock;
 };
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 3c7b9d9..09e41ff 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -22,6 +22,7 @@
 #include <cinttypes>
 
 #include <android-base/stringprintf.h>
+#include <gui/TraceUtils.h>
 #include <renderengine/impl/ExternalTexture.h>
 
 #include "ClientCache.h"
@@ -30,18 +31,18 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
 
-ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
+ClientCache::ClientCache() : mDeathRecipient(sp<CacheDeathRecipient>::make()) {}
 
 bool ClientCache::getBuffer(const client_cache_t& cacheId,
                             ClientCacheBuffer** outClientCacheBuffer) {
     auto& [processToken, id] = cacheId;
     if (processToken == nullptr) {
-        ALOGE("failed to get buffer, invalid (nullptr) process token");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid (nullptr) process token");
         return false;
     }
     auto it = mBuffers.find(processToken);
     if (it == mBuffers.end()) {
-        ALOGE("failed to get buffer, invalid process token");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid process token");
         return false;
     }
 
@@ -49,7 +50,7 @@
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
-        ALOGV("failed to get buffer, invalid buffer id");
+        ALOGE_AND_TRACE("ClientCache::getBuffer - invalid buffer id");
         return false;
     }
 
@@ -58,16 +59,17 @@
     return true;
 }
 
-bool ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
+base::expected<std::shared_ptr<renderengine::ExternalTexture>, ClientCache::AddError>
+ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
     auto& [processToken, id] = cacheId;
     if (processToken == nullptr) {
-        ALOGE("failed to cache buffer: invalid process token");
-        return false;
+        ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token");
+        return base::unexpected(AddError::Unspecified);
     }
 
     if (!buffer) {
-        ALOGE("failed to cache buffer: invalid buffer");
-        return false;
+        ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer");
+        return base::unexpected(AddError::Unspecified);
     }
 
     std::lock_guard lock(mMutex);
@@ -79,16 +81,16 @@
     if (it == mBuffers.end()) {
         token = processToken.promote();
         if (!token) {
-            ALOGE("failed to cache buffer: invalid token");
-            return false;
+            ALOGE_AND_TRACE("ClientCache::add - invalid token");
+            return base::unexpected(AddError::Unspecified);
         }
 
         // Only call linkToDeath if not a local binder
         if (token->localBinder() == nullptr) {
             status_t err = token->linkToDeath(mDeathRecipient);
             if (err != NO_ERROR) {
-                ALOGE("failed to cache buffer: could not link to death");
-                return false;
+                ALOGE_AND_TRACE("ClientCache::add - could not link to death");
+                return base::unexpected(AddError::Unspecified);
             }
         }
         auto [itr, success] =
@@ -102,21 +104,22 @@
     auto& processBuffers = it->second.second;
 
     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
-        ALOGE("failed to cache buffer: cache is full");
-        return false;
+        ALOGE_AND_TRACE("ClientCache::add - cache is full");
+        return base::unexpected(AddError::CacheFull);
     }
 
     LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
                         "Attempted to build the ClientCache before a RenderEngine instance was "
                         "ready!");
-    processBuffers[id].buffer = std::make_shared<
-            renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
-                                                 renderengine::impl::ExternalTexture::Usage::
-                                                         READABLE);
-    return true;
+
+    return (processBuffers[id].buffer = std::make_shared<
+                    renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::READABLE));
 }
 
-void ClientCache::erase(const client_cache_t& cacheId) {
+sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
+    sp<GraphicBuffer> buffer;
     auto& [processToken, id] = cacheId;
     std::vector<sp<ErasedRecipient>> pendingErase;
     {
@@ -124,9 +127,11 @@
         ClientCacheBuffer* buf = nullptr;
         if (!getBuffer(cacheId, &buf)) {
             ALOGE("failed to erase buffer, could not retrieve buffer");
-            return;
+            return nullptr;
         }
 
+        buffer = buf->buffer->getBuffer();
+
         for (auto& recipient : buf->recipients) {
             sp<ErasedRecipient> erasedRecipient = recipient.promote();
             if (erasedRecipient) {
@@ -140,6 +145,7 @@
     for (auto& recipient : pendingErase) {
         recipient->bufferErased(cacheId);
     }
+    return buffer;
 }
 
 std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index a9b8177..b56b252 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -33,12 +33,27 @@
 
 namespace android {
 
+// This class manages a cache of buffer handles between SurfaceFlinger clients
+// and the SurfaceFlinger process which optimizes away some of the cost of
+// sending buffer handles across processes.
+//
+// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When
+// a buffer is uncached, it is not only purged from this cache, but the buffer
+// ID is also passed down to CompositionEngine to purge it from a similar cache
+// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge
+// both the SurfaceFlinger side of this other cache, as well as Composer HAL's
+// side of the cache.
+//
 class ClientCache : public Singleton<ClientCache> {
 public:
     ClientCache();
 
-    bool add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
-    void erase(const client_cache_t& cacheId);
+    enum class AddError { CacheFull, Unspecified };
+
+    base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
+            const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
+
+    sp<GraphicBuffer> erase(const client_cache_t& cacheId);
 
     std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
 
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 11a9e19..f3a0186 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -9,7 +9,11 @@
 
 cc_defaults {
     name: "libcompositionengine_defaults",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "android.hardware.graphics.composer3-ndk_shared",
+        "librenderengine_deps",
+        "surfaceflinger_defaults",
+    ],
     cflags: [
         "-DLOG_TAG=\"CompositionEngine\"",
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
@@ -20,10 +24,9 @@
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V4-cpp",
         "libbase",
         "libcutils",
         "libgui",
@@ -40,7 +43,6 @@
         "libmath",
         "librenderengine",
         "libtonemap",
-        "libtrace_proto",
         "libaidlcommonsupport",
         "libprocessgroup",
         "libcgrouprc",
@@ -139,6 +141,11 @@
         "libgmock",
         "libgtest",
     ],
+    // For some reason, libvulkan isn't picked up from librenderengine
+    // Probably ASAN related?
+    shared_libs: [
+        "libvulkan",
+    ],
     sanitize: {
         // By using the address sanitizer, we not only uncover any issues
         // with the test, but also any issues with the code under test.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 3faa068..7c10fa5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -18,9 +18,10 @@
 
 #include <TimeStats/TimeStats.h>
 #include <utils/Timers.h>
-
 #include <memory>
 
+#include "Feature.h"
+
 namespace android {
 
 class HWComposer;
@@ -55,9 +56,9 @@
     virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
 
     virtual renderengine::RenderEngine& getRenderEngine() const = 0;
-    virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+    virtual void setRenderEngine(renderengine::RenderEngine*) = 0;
 
-    virtual TimeStats& getTimeStats() const = 0;
+    virtual TimeStats* getTimeStats() const = 0;
     virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
 
     virtual bool needsAnotherUpdate() const = 0;
@@ -72,6 +73,8 @@
     // TODO(b/121291683): These will become private/internal
     virtual void preComposition(CompositionRefreshArgs&) = 0;
 
+    virtual FeatureFlags getFeatureFlags() const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f201751..d93e25e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -32,6 +32,11 @@
 using Layers = std::vector<sp<compositionengine::LayerFE>>;
 using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
 
+struct BorderRenderInfo {
+    float width = 0;
+    half4 color;
+    std::vector<int32_t> layerIds;
+};
 /**
  * A parameter object for refreshing a set of outputs
  */
@@ -47,6 +52,9 @@
     // All the layers that have queued updates.
     Layers layersWithQueuedFrames;
 
+    // All graphic buffers that will no longer be used and should be removed from caches.
+    std::vector<uint64_t> bufferIdsToUncache;
+
     // Controls how the color mode is chosen for an output
     OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
 
@@ -59,9 +67,6 @@
     // Used to correctly apply an inverse-display buffer transform if applicable
     ui::Transform::RotationFlags internalDisplayRotationFlags{ui::Transform::ROT_0};
 
-    // If true, GPU clocks will be increased when rendering blurs
-    bool blursAreExpensive{false};
-
     // If true, the complete output geometry needs to be recomputed this frame
     bool updatingOutputGeometryThisFrame{false};
 
@@ -78,18 +83,19 @@
     // If set, causes the dirty regions to flash with the delay
     std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
 
-    // The earliest time to send the present command to the HAL
-    std::chrono::steady_clock::time_point earliestPresentTime;
-
-    // The previous present fence. Used together with earliestPresentTime
-    // to prevent an early presentation of a frame.
-    std::shared_ptr<FenceTime> previousPresentFence;
+    // Optional.
+    // The earliest time to send the present command to the HAL.
+    std::optional<std::chrono::steady_clock::time_point> earliestPresentTime;
 
     // The expected time for the next present
     nsecs_t expectedPresentTime{0};
 
     // If set, a frame has been scheduled for that time.
     std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
+
+    std::vector<BorderRenderInfo> borderInfoList;
+
+    bool hasTrustedPresentationListener = false;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
similarity index 66%
rename from services/surfaceflinger/CompositionEngine/tests/TestUtils.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
index c80fde6..ee8000a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Feature.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -13,19 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-#include <future>
+#include <ftl/flags.h>
+#include <cstdint>
 
 namespace android::compositionengine {
-namespace {
 
-template <class T>
-std::future<T> futureOf(T obj) {
-    std::promise<T> resultPromise;
-    std::future<T> resultFuture = resultPromise.get_future();
-    resultPromise.set_value(std::move(obj));
-    return resultFuture;
-}
-} // namespace
-} // namespace android::compositionengine
+enum class Feature : int32_t {
+    kSnapshotLayerMetadata = 1 << 0,
+};
+
+using FeatureFlags = ftl::Flags<Feature>;
+
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h
deleted file mode 100644
index 0ce263b..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/FenceResult.h
+++ /dev/null
@@ -1,49 +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 <android-base/expected.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-// TODO(b/232535621): Pull this file to <ui/FenceResult.h> so that RenderEngine::drawLayers returns
-// FenceResult rather than RenderEngineResult.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <renderengine/RenderEngine.h>
-#pragma clang diagnostic pop
-
-namespace android {
-
-class Fence;
-
-using FenceResult = base::expected<sp<Fence>, status_t>;
-
-// TODO(b/232535621): Prevent base::unexpected(NO_ERROR) from being a valid FenceResult.
-inline status_t fenceStatus(const FenceResult& fenceResult) {
-    return fenceResult.ok() ? NO_ERROR : fenceResult.error();
-}
-
-inline FenceResult toFenceResult(renderengine::RenderEngineResult&& result) {
-    if (auto [status, fence] = std::move(result); fence.ok()) {
-        return sp<Fence>::make(std::move(fence));
-    } else {
-        return base::unexpected(status);
-    }
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index ec610c1..ccff1ec 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -19,8 +19,7 @@
 #include <optional>
 #include <ostream>
 #include <unordered_set>
-
-#include <compositionengine/FenceResult.h>
+#include "ui/LayerStack.h"
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -33,6 +32,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <ftl/future.h>
+#include <ui/FenceResult.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
@@ -40,6 +40,10 @@
 
 class Fence;
 
+namespace gui {
+struct LayerMetadata;
+}
+
 namespace compositionengine {
 
 struct LayerFECompositionState;
@@ -54,31 +58,8 @@
     // Called before composition starts. Should return true if this layer has
     // pending updates which would require an extra display refresh cycle to
     // process.
-    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
-
-    // Used with latchCompositionState()
-    enum class StateSubset {
-        // Gets the basic geometry (bounds, transparent region, visibility,
-        // transforms, alpha) for the layer, for computing visibility and
-        // coverage.
-        BasicGeometry,
-
-        // Gets the full geometry (crops, buffer transforms, metadata) and
-        // content (buffer or color) state for the layer.
-        GeometryAndContent,
-
-        // Gets the per frame content (buffer or color) state for the layer.
-        Content,
-
-        // Gets the cursor state for the layer.
-        Cursor,
-    };
-
-    // Prepares the output-independent composition state for the layer. The
-    // StateSubset argument selects what portion of the state is actually needed
-    // by the CompositionEngine code, since computing everything may be
-    // expensive.
-    virtual void prepareCompositionState(StateSubset) = 0;
+    virtual bool onPreComposition(nsecs_t refreshStartTime,
+                                  bool updatingOutputGeometryThisFrame) = 0;
 
     struct ClientCompositionTargetSettings {
         enum class BlurSetting {
@@ -138,6 +119,9 @@
 
         // Requested white point of the layer in nits
         const float whitePointNits;
+
+        // True if layers with 170M dataspace should be overridden to sRGB.
+        const bool treat170mAsSrgb;
     };
 
     // A superset of LayerSettings required by RenderEngine to compose a layer
@@ -150,14 +134,14 @@
         uint64_t frameNumber = 0;
     };
 
-    // Returns the z-ordered list of LayerSettings to pass to RenderEngine::drawLayers. The list
-    // may contain shadows casted by the layer or the content of the layer itself.  If the layer
-    // does not render then an empty list will be returned.
-    virtual std::vector<LayerSettings> prepareClientCompositionList(
-            ClientCompositionTargetSettings&) = 0;
+    // Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows
+    // casted by the layer or the content of the layer itself. If the layer does not render then an
+    // empty optional will be returned.
+    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>) = 0;
+    virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
 
     // Gets some kind of identifier for the layer for debug purposes.
     virtual const char* getDebugName() const = 0;
@@ -168,6 +152,8 @@
     // Whether the layer should be rendered with rounded corners.
     virtual bool hasRoundedCorners() const = 0;
     virtual void setWasClientComposed(const sp<Fence>&) {}
+    virtual const gui::LayerMetadata* getMetadata() const = 0;
+    virtual const gui::LayerMetadata* getRelativeMetadata() const = 0;
 };
 
 // TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 974f7c6..35ca3a5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,6 +18,7 @@
 
 #include <cstdint>
 
+#include <android/gui/CachingHint.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/BlurRegion.h>
@@ -163,7 +164,6 @@
 
     // The buffer and related state
     sp<GraphicBuffer> buffer;
-    int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
     sp<Fence> acquireFence = Fence::NO_FENCE;
     Region surfaceDamage;
     uint64_t frameNumber = 0;
@@ -210,6 +210,10 @@
     // The dimming flag
     bool dimmingEnabled{true};
 
+    float currentHdrSdrRatio = 1.f;
+    float desiredHdrSdrRatio = 1.f;
+
+    gui::CachingHint cachingHint = gui::CachingHint::Enabled;
     virtual ~LayerFECompositionState();
 
     // Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 2203639..a3d8639 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,6 +23,7 @@
 #include <type_traits>
 #include <unordered_map>
 #include <utility>
+#include <vector>
 
 #include <compositionengine/LayerFE.h>
 #include <renderengine/LayerSettings.h>
@@ -152,6 +153,10 @@
         Region aboveOpaqueLayers;
         // The region of the output which should be considered dirty
         Region dirtyRegion;
+        // The region of the output which is covered by layers, excluding display overlays. This
+        // only has a value if there's something needing it, like when a TrustedPresentationListener
+        // is set
+        std::optional<Region> aboveCoveredLayersExcludingOverlays;
     };
 
     virtual ~Output();
@@ -262,9 +267,6 @@
     // Presents the output, finalizing all composition details
     virtual void present(const CompositionRefreshArgs&) = 0;
 
-    // Latches the front-end layer state for each output layer
-    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
-
     // Enables predicting composition strategy to run client composition earlier
     virtual void setPredictCompositionStrategy(bool) = 0;
 
@@ -275,6 +277,7 @@
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
 
+    virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0;
     virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
     virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
     virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
@@ -291,12 +294,11 @@
     using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
     // Runs prepare frame in another thread while running client composition using
     // the previous frame's composition strategy.
-    virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
+    virtual GpuCompositionResult prepareFrameAsync() = 0;
     virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
-    virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
+    virtual void finishFrame(GpuCompositionResult&&) = 0;
     virtual std::optional<base::unique_fd> composeSurfaces(
-            const Region&, const compositionengine::CompositionRefreshArgs&,
-            std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
+            const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
     virtual void postFramebuffer() = 0;
     virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
     virtual bool chooseCompositionStrategy(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index bf5184e..4dbf8d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
 #include <cstdint>
 #include <optional>
 #include <string>
+#include <vector>
 
 #include <ui/Transform.h>
 #include <utils/StrongPointer.h>
@@ -81,6 +82,10 @@
     // TODO(lpique): Make this protected once it is only internally called.
     virtual CompositionState& editState() = 0;
 
+    // Clear the cache entries for a set of buffers that SurfaceFlinger no
+    // longer cares about.
+    virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 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
@@ -126,9 +131,9 @@
     // Returns true if the composition settings scale pixels
     virtual bool needsFiltering() const = 0;
 
-    // Returns a composition list to be used by RenderEngine if the layer has been overridden
+    // Returns LayerSettings to be used by RenderEngine if the layer has been overridden
     // during the composition process
-    virtual std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const = 0;
+    virtual std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index 9ee779c..5854674 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -91,16 +91,9 @@
     // Called after the HWC calls are made to present the display
     virtual void onPresentDisplayCompleted() = 0;
 
-    // Called after the surface has been rendering to signal the surface should
-    // be made ready for displaying
-    virtual void flip() = 0;
-
     // Debugging - Dumps the state of the RenderSurface to a string
     virtual void dump(std::string& result) const = 0;
 
-    // Debugging - gets the page flip count for the RenderSurface
-    virtual std::uint32_t getPageFlipCount() const = 0;
-
     // Returns true if the render surface supports client composition prediction.
     virtual bool supportsCompositionStrategyPrediction() const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index 386808d..c699557 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -34,9 +34,9 @@
     void setHwComposer(std::unique_ptr<HWComposer>) override;
 
     renderengine::RenderEngine& getRenderEngine() const override;
-    void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+    void setRenderEngine(renderengine::RenderEngine*) override;
 
-    TimeStats& getTimeStats() const override;
+    TimeStats* getTimeStats() const override;
     void setTimeStats(const std::shared_ptr<TimeStats>&) override;
 
     bool needsAnotherUpdate() const override;
@@ -48,17 +48,17 @@
 
     void preComposition(CompositionRefreshArgs&) override;
 
+    FeatureFlags getFeatureFlags() const override;
+
     // Debugging
     void dump(std::string&) const override;
 
-    void updateLayerStateFromFE(CompositionRefreshArgs& args);
-
     // Testing
     void setNeedsAnotherUpdateForTest(bool);
 
 private:
     std::unique_ptr<HWComposer> mHwComposer;
-    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    renderengine::RenderEngine* mRenderEngine;
     std::shared_ptr<TimeStats> mTimeStats;
     bool mNeedsAnotherUpdate = false;
     nsecs_t mRefreshStartTime = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 33a10a3..6cf1d68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -61,7 +61,7 @@
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     void setExpensiveRenderingExpected(bool) override;
-    void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+    void finishFrame(GpuCompositionResult&&) override;
 
     // compositionengine::Display overrides
     DisplayId getId() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index fd22aa3..b6a4240 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -17,7 +17,8 @@
 #pragma once
 
 #include <cstdint>
-#include <vector>
+#include <stack>
+#include <unordered_map>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -37,35 +38,76 @@
 
 namespace compositionengine::impl {
 
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer.  When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache.  The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HwcBufferCache {
-public:
-    HwcBufferCache();
-    // Given a buffer, return the HWC cache slot and
-    // buffer to be sent to HWC.
-    //
-    // outBuffer is set to buffer when buffer is not in the HWC cache;
-    // otherwise, outBuffer is set to nullptr.
-    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
-                      sp<GraphicBuffer>* outBuffer);
+// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases
+// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as
+// an optimization.
+struct HwcSlotAndBuffer {
+    uint32_t slot;
+    sp<GraphicBuffer> buffer;
+};
 
-    // Special caching slot for the layer caching feature.
-    static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+//
+// Manages the slot assignments for a buffers stored in Composer HAL's cache.
+//
+// Cache slots are an optimization when communicating buffer handles to Composer
+// HAL. When updating a layer's buffer, we can either send a new buffer handle
+// along with it's slot assignment or request the HAL to reuse a buffer handle
+// that we've already sent by using the slot assignment. The latter is cheaper
+// since it eliminates the overhead to transfer the buffer handle over IPC and
+// the overhead for the HAL to clone the handle.
+//
+class HwcBufferCache {
+private:
+    static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+
+public:
+    // public for testing
+    // Override buffers don't use the normal cache slots because we don't want them to evict client
+    // buffers from the cache. We add an extra slot at the end for the override buffers.
+    static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount;
+
+    HwcBufferCache();
+
+    //
+    // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+    //
+    // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+    // buffer handle.
+    //
+    HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+    //
+    // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+    //
+    // A special slot number is used for override buffers.
+    //
+    // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+    // buffer handle.
+    //
+    HwcSlotAndBuffer getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+
+    //
+    // When a client process discards a buffer, it needs to be purged from the HWC cache.
+    //
+    // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache.
+    //
+    uint32_t uncache(uint64_t graphicBufferId);
 
 private:
-    // an array where the index corresponds to a slot and the value corresponds to a (counter,
-    // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
-    // or used and allows us to keep track of the least-recently used buffer.
-    static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
-    wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
+    uint32_t cache(const sp<GraphicBuffer>& buffer);
+    uint32_t getLeastRecentlyUsedSlot();
+
+    struct Cache {
+        sp<GraphicBuffer> buffer;
+        uint32_t slot;
+        // Cache entries are evicted according to least-recently-used when more than
+        // kMaxLayerBufferCount unique buffers have been sent to a layer.
+        uint64_t lruCounter;
+    };
+
+    std::unordered_map<uint64_t, Cache> mCacheByBufferId;
+    sp<GraphicBuffer> mLastOverrideBuffer;
+    std::stack<uint32_t> mFreeSlots;
+    uint64_t mLeastRecentlyUsedCounter;
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 428c19f..229a657 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -82,6 +82,7 @@
     void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
     void present(const CompositionRefreshArgs&) override;
 
+    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
     void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
     void collectVisibleLayers(const CompositionRefreshArgs&,
                               compositionengine::Output::CoverageState&) override;
@@ -89,18 +90,16 @@
                                     compositionengine::Output::CoverageState&) override;
     void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
 
-    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
     void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void planComposition() override;
     void writeCompositionState(const compositionengine::CompositionRefreshArgs&) override;
     void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
     void beginFrame() override;
     void prepareFrame() override;
-    GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
+    GpuCompositionResult prepareFrameAsync() override;
     void devOptRepaintFlash(const CompositionRefreshArgs&) override;
-    void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+    void finishFrame(GpuCompositionResult&&) override;
     std::optional<base::unique_fd> composeSurfaces(const Region&,
-                                                   const compositionengine::CompositionRefreshArgs&,
                                                    std::shared_ptr<renderengine::ExternalTexture>,
                                                    base::unique_fd&) override;
     void postFramebuffer() override;
@@ -135,9 +134,10 @@
     void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+    virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings() const;
     std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
-          bool supportsProtectedContent, ui::Dataspace outputDataspace,
-          std::vector<LayerFE*> &outLayerFEs) override;
+            bool supportsProtectedContent, ui::Dataspace outputDataspace,
+            std::vector<LayerFE*>& outLayerFEs) override;
     void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
     void setExpensiveRenderingExpected(bool enabled) override;
     void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
@@ -154,8 +154,11 @@
 
     bool mustRecompose() const;
 
+    const std::string& getNamePlusId() const { return mNamePlusId; }
+
 private:
     void dirtyEntireOutput();
+    void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&);
     compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
     void finishPrepareFrame();
     ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
@@ -163,6 +166,7 @@
             const compositionengine::CompositionRefreshArgs&) const;
 
     std::string mName;
+    std::string mNamePlusId;
 
     std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
     std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 7709b96..a3fda61 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -33,6 +33,7 @@
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
 #include <compositionengine/ProjectionSpace.h>
+#include <renderengine/BorderRenderInfo.h>
 #include <ui/LayerStack.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -121,12 +122,9 @@
 
     bool previousDeviceRequestedSuccess = false;
 
+    // Optional.
     // The earliest time to send the present command to the HAL
-    std::chrono::steady_clock::time_point earliestPresentTime;
-
-    // The previous present fence. Used together with earliestPresentTime
-    // to prevent an early presentation of a frame.
-    std::shared_ptr<FenceTime> previousPresentFence;
+    std::optional<std::chrono::steady_clock::time_point> earliestPresentTime;
 
     // The expected time for the next present
     nsecs_t expectedPresentTime{0};
@@ -164,6 +162,8 @@
 
     bool treat170mAsSrgb = false;
 
+    std::vector<renderengine::BorderRenderInfo> borderInfoList;
+
     uint64_t lastOutputLayerHash = 0;
     uint64_t outputLayerHash = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index ecd432f..f383392 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -18,7 +18,9 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
+#include <vector>
 
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/OutputLayer.h>
@@ -43,6 +45,8 @@
 
     void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
+    void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+
     void updateCompositionState(bool includeGeometry, bool forceClientComposition,
                                 ui::Transform::RotationFlags) override;
     void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
@@ -57,7 +61,7 @@
     void prepareForDeviceLayerRequests() override;
     void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
     bool needsFiltering() const override;
-    std::vector<LayerFE::LayerSettings> getOverrideCompositionList() const override;
+    std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
 
     void dump(std::string&) const override;
     virtual FloatRect calculateOutputSourceCrop(uint32_t internalDisplayRotationFlags) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 2b383c1..7b0af3a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -63,9 +63,15 @@
     // The portion of the layer that is not obscured and is also opaque
     Region visibleNonTransparentRegion;
 
-    // The portion of the layer that is obscured by opaque layers on top
+    // The portion of the layer that is obscured by all layers on top. This includes transparent and
+    // opaque.
     Region coveredRegion;
 
+    // The portion of the layer that is obscured by all layers on top excluding display overlays.
+    // This only has a value if there's something needing it, like when a
+    // TrustedPresentationListener is set.
+    std::optional<Region> coveredRegionExcludingDisplayOverlays;
+
     // The visibleRegion transformed to output space
     Region outputSpaceVisibleRegion;
 
@@ -136,6 +142,10 @@
         // cost of sending reused buffers to the HWC.
         HwcBufferCache hwcBufferCache;
 
+        // The previously-active buffer for this layer.
+        uint64_t activeBufferId;
+        uint32_t activeBufferSlot;
+
         // Set to true when overridden info has been sent to HW composer
         bool stateOverridden = false;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index e4cb113..1c14a43 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -62,15 +62,12 @@
             base::unique_fd* bufferFence) override;
     void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
-    void flip() override;
     bool supportsCompositionStrategyPrediction() const override;
 
     // Debugging
     void dump(std::string& result) const override;
-    std::uint32_t getPageFlipCount() const override;
 
     // Testing
-    void setPageFlipCountForTest(std::uint32_t);
     void setSizeForTest(const ui::Size&);
     std::shared_ptr<renderengine::ExternalTexture>& mutableTextureForTest();
     base::unique_fd& mutableBufferReadyForTest();
@@ -89,7 +86,6 @@
     ui::Size mSize;
     const size_t mMaxTextureCacheSize;
     bool mProtected{false};
-    std::uint32_t mPageFlipCount{0};
 };
 
 std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 24a7744..d26ca9d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -149,6 +149,9 @@
 
     bool hasSolidColorLayers() const;
 
+    // True if any layer in this cached set has CachingHint::Disabled
+    bool cachingHintExcludesLayers() const;
+
 private:
     const NonBufferHash mFingerprint;
     std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5aec7c2..ce2b96f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -72,6 +72,8 @@
     SolidColor            = 1u << 16,
     BackgroundBlurRadius  = 1u << 17,
     BlurRegions           = 1u << 18,
+    HasProtectedContent   = 1u << 19,
+    CachingHint           = 1u << 20,
 };
 // clang-format on
 
@@ -245,9 +247,15 @@
 
     ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
 
-    bool isProtected() const {
-        return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
-    }
+    float getHdrSdrRatio() const {
+        return getOutputLayer()->getLayerFE().getCompositionState()->currentHdrSdrRatio;
+    };
+
+    wp<GraphicBuffer> getBuffer() const { return mBuffer.get(); }
+
+    bool isProtected() const { return mIsProtected.get(); }
+
+    gui::CachingHint getCachingHint() const { return mCachingHint.get(); }
 
     bool hasSolidColorCompositionType() const {
         return getOutputLayer()->getLayerFE().getCompositionState()->compositionType ==
@@ -482,7 +490,19 @@
                                       return hash;
                                   }};
 
-    static const constexpr size_t kNumNonUniqueFields = 17;
+    OutputLayerState<bool, LayerStateField::HasProtectedContent> mIsProtected{[](auto layer) {
+        return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+    }};
+
+    OutputLayerState<gui::CachingHint, LayerStateField::CachingHint>
+            mCachingHint{[](auto layer) {
+                             return layer->getLayerFE().getCompositionState()->cachingHint;
+                         },
+                         [](const gui::CachingHint& cachingHint) {
+                             return std::vector<std::string>{toString(cachingHint)};
+                         }};
+
+    static const constexpr size_t kNumNonUniqueFields = 19;
 
     std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
         std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -496,13 +516,11 @@
     }
 
     std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const {
-        return {
-                &mDisplayFrame, &mSourceCrop,     &mBufferTransform,      &mBlendMode,
+        return {&mDisplayFrame, &mSourceCrop,     &mBufferTransform,      &mBlendMode,
                 &mAlpha,        &mLayerMetadata,  &mVisibleRegion,        &mOutputDataspace,
                 &mPixelFormat,  &mColorTransform, &mCompositionType,      &mSidebandStream,
                 &mBuffer,       &mSolidColor,     &mBackgroundBlurRadius, &mBlurRegions,
-                &mFrameNumber,
-        };
+                &mFrameNumber,  &mIsProtected,    &mCachingHint};
     }
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index f953d0b..9b2387b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -40,9 +40,9 @@
     MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
 
     MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
-    MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+    MOCK_METHOD1(setRenderEngine, void(renderengine::RenderEngine*));
 
-    MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+    MOCK_CONST_METHOD0(getTimeStats, TimeStats*());
     MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
 
     MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
@@ -53,6 +53,8 @@
 
     MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
 
+    MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
+
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 1c5c10f..15e4577 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -36,24 +36,27 @@
     // sp<StrictMock<LayerFE>>::make()
     friend class sp<LayerFE>;
     friend class testing::StrictMock<LayerFE>;
+    friend class testing::NiceMock<LayerFE>;
 
 public:
     virtual ~LayerFE();
 
     MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
 
-    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+    MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool));
 
-    MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
-    MOCK_METHOD1(prepareClientCompositionList,
-                 std::vector<compositionengine::LayerFE::LayerSettings>(
-                         compositionengine::LayerFE::ClientCompositionTargetSettings&));
+    MOCK_CONST_METHOD1(prepareClientComposition,
+                       std::optional<compositionengine::LayerFE::LayerSettings>(
+                               compositionengine::LayerFE::ClientCompositionTargetSettings&));
 
-    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>), (override));
+    MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
+                (override));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
     MOCK_CONST_METHOD0(getSequence, int32_t());
     MOCK_CONST_METHOD0(hasRoundedCorners, bool());
+    MOCK_CONST_METHOD0(getMetadata, gui::LayerMetadata*());
+    MOCK_CONST_METHOD0(getRelativeMetadata, gui::LayerMetadata*());
 };
 
 } // 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 2a04949..a56fc79 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -82,6 +82,7 @@
     MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
     MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
 
+    MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
     MOCK_METHOD2(rebuildLayerStacks,
                  void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
     MOCK_METHOD2(collectVisibleLayers,
@@ -91,7 +92,6 @@
                  void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
     MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
     MOCK_METHOD1(updateCompositionState, void(const CompositionRefreshArgs&));
     MOCK_METHOD0(planComposition, void());
     MOCK_METHOD1(writeCompositionState, void(const CompositionRefreshArgs&));
@@ -100,7 +100,7 @@
     MOCK_METHOD0(beginFrame, void());
 
     MOCK_METHOD0(prepareFrame, void());
-    MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+    MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
     MOCK_METHOD1(chooseCompositionStrategy,
                  bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD1(chooseCompositionStrategyAsync,
@@ -110,14 +110,12 @@
 
     MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
 
-    MOCK_METHOD2(finishFrame,
-                 void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
+    MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
 
-    MOCK_METHOD4(composeSurfaces,
-                 std::optional<base::unique_fd>(
-                         const Region&,
-                         const compositionengine::CompositionRefreshArgs& refreshArgs,
-                         std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+    MOCK_METHOD3(composeSurfaces,
+                 std::optional<base::unique_fd>(const Region&,
+                                                std::shared_ptr<renderengine::ExternalTexture>,
+                                                base::unique_fd&));
     MOCK_CONST_METHOD0(getSkipColorTransform, bool());
 
     MOCK_METHOD0(postFramebuffer, void());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index a6cb811..5fef63a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <optional>
+
 #include <compositionengine/CompositionEngine.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
@@ -33,6 +35,8 @@
 
     MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
 
+    MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
+
     MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
     MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
 
@@ -51,7 +55,7 @@
     MOCK_METHOD0(prepareForDeviceLayerRequests, void());
     MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
     MOCK_CONST_METHOD0(needsFiltering, bool());
-    MOCK_CONST_METHOD0(getOverrideCompositionList, std::vector<LayerFE::LayerSettings>());
+    MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index e12aebb..af8d4bc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -42,9 +42,7 @@
     MOCK_METHOD1(dequeueBuffer, std::shared_ptr<renderengine::ExternalTexture>(base::unique_fd*));
     MOCK_METHOD1(queueBuffer, void(base::unique_fd));
     MOCK_METHOD0(onPresentDisplayCompleted, void());
-    MOCK_METHOD0(flip, void());
     MOCK_CONST_METHOD1(dump, void(std::string& result));
-    MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
     MOCK_CONST_METHOD0(supportsCompositionStrategyPrediction, bool());
 };
 
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index 6203dc6..15fadbc 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -65,15 +65,15 @@
 }
 
 renderengine::RenderEngine& CompositionEngine::getRenderEngine() const {
-    return *mRenderEngine.get();
+    return *mRenderEngine;
 }
 
-void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-    mRenderEngine = std::move(renderEngine);
+void CompositionEngine::setRenderEngine(renderengine::RenderEngine* renderEngine) {
+    mRenderEngine = renderEngine;
 }
 
-TimeStats& CompositionEngine::getTimeStats() const {
-    return *mTimeStats.get();
+TimeStats* CompositionEngine::getTimeStats() const {
+    return mTimeStats.get();
 }
 
 void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
@@ -105,8 +105,6 @@
         }
     }
 
-    updateLayerStateFromFE(args);
-
     for (const auto& output : args.outputs) {
         output->present(args);
     }
@@ -119,8 +117,6 @@
     for (const auto& output : args.outputs) {
         for (auto* layer : output->getOutputLayersOrderedByZ()) {
             if (layer->isHardwareCursor()) {
-                // Latch the cursor composition state from each front-end layer.
-                layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
                 layer->writeCursorPositionToHWC();
             }
         }
@@ -136,7 +132,7 @@
     mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     for (auto& layer : args.layers) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
+        if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) {
             needsAnotherUpdate = true;
         }
     }
@@ -144,6 +140,10 @@
     mNeedsAnotherUpdate = needsAnotherUpdate;
 }
 
+FeatureFlags CompositionEngine::getFeatureFlags() const {
+    return {};
+}
+
 void CompositionEngine::dump(std::string&) const {
     // The base class has no state to dump, but derived classes might.
 }
@@ -152,12 +152,5 @@
     mNeedsAnotherUpdate = value;
 }
 
-void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
-    // Update the composition state from each front-end layer
-    for (const auto& output : args.outputs) {
-        output->updateLayerStateFromFE(args);
-    }
-}
-
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 163d9a3..85fc095 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -25,6 +25,7 @@
 #include <compositionengine/impl/DumpHelpers.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/RenderSurface.h>
+#include <gui/TraceUtils.h>
 
 #include <utils/Trace.h>
 
@@ -196,7 +197,7 @@
                             });
 
         if (hasQueuedFrames) {
-            releasedLayers.emplace_back(layerFE);
+            releasedLayers.emplace_back(wp<LayerFE>::fromExisting(layerFE));
         }
     }
 
@@ -235,7 +236,7 @@
 
 bool Display::chooseCompositionStrategy(
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s for %s", __func__, getNamePlusId().c_str());
     ALOGV(__FUNCTION__);
 
     if (mIsDisconnected) {
@@ -248,16 +249,20 @@
         return false;
     }
 
-    const nsecs_t startTime = systemTime();
-
     // Get any composition changes requested by the HWC device, and apply them.
     std::optional<android::HWComposer::DeviceRequestedChanges> changes;
     auto& hwc = getCompositionEngine().getHwComposer();
     const bool requiresClientComposition = anyLayersRequireClientComposition();
+
+    if (isPowerHintSessionEnabled()) {
+        mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+    }
+
+    const TimePoint hwcValidateStartTime = TimePoint::now();
+
     if (status_t result =
                 hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
                                                 getState().earliestPresentTime,
-                                                getState().previousPresentFence,
                                                 getState().expectedPresentTime, outChanges);
         result != NO_ERROR) {
         ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
@@ -266,8 +271,10 @@
     }
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime());
-        mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
+        mPowerAdvisor->setHwcValidateTiming(mId, hwcValidateStartTime, TimePoint::now());
+        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
+            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
+        }
     }
 
     return true;
@@ -370,21 +377,16 @@
 
     auto& hwc = getCompositionEngine().getHwComposer();
 
-    const nsecs_t startTime = systemTime();
+    const TimePoint startTime = TimePoint::now();
 
-    if (isPowerHintSessionEnabled()) {
-        if (!getCompositionEngine().getHwComposer().getComposer()->isSupported(
-                    Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
-            getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
-            mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime);
-        }
+    if (isPowerHintSessionEnabled() && getState().earliestPresentTime) {
+        mPowerAdvisor->setHwcPresentDelayedTime(mId, *getState().earliestPresentTime);
     }
 
-    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime,
-                                   getState().previousPresentFence);
+    hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime);
 
     if (isPowerHintSessionEnabled()) {
-        mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime());
+        mPowerAdvisor->setHwcPresentTiming(mId, startTime, TimePoint::now());
     }
 
     fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt);
@@ -420,8 +422,7 @@
     mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
 }
 
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
-                          GpuCompositionResult&& result) {
+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
     //    keep its virtual display state machine in sync, or
@@ -431,14 +432,7 @@
         return;
     }
 
-    impl::Output::finishFrame(refreshArgs, std::move(result));
-
-    if (isPowerHintSessionEnabled()) {
-        auto& hwc = getCompositionEngine().getHwComposer();
-        if (auto halDisplayId = HalDisplayId::tryCast(mId)) {
-            mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId));
-        }
-    }
+    impl::Output::finishFrame(std::move(result));
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f95382d..f0105b2 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -16,43 +16,75 @@
 
 #include <compositionengine/impl/HwcBufferCache.h>
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
-    std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
+    for (uint32_t i = kMaxLayerBufferCount; i-- > 0;) {
+        mFreeSlots.push(i);
+    }
 }
 
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
-                                  sp<GraphicBuffer>* outBuffer) {
-    // default is 0
-    if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
-        slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
-        *outSlot = 0;
-    } else {
-        *outSlot = static_cast<uint32_t>(slot);
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+    if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
+        Cache& cache = i->second;
+        // mark this cache slot as more recently used so it won't get evicted anytime soon
+        cache.lruCounter = mLeastRecentlyUsedCounter++;
+        return {cache.slot, nullptr};
     }
+    return {cache(buffer), buffer};
+}
 
-    auto& currentBuffer = mBuffers[*outSlot];
-    wp<GraphicBuffer> weakCopy(buffer);
-    if (currentBuffer == weakCopy) {
-        // already cached in HWC, skip sending the buffer
-        *outBuffer = nullptr;
-    } else {
-        *outBuffer = buffer;
-
-        // update cache
-        currentBuffer = buffer;
+HwcSlotAndBuffer HwcBufferCache::getOverrideHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+    if (buffer == mLastOverrideBuffer) {
+        return {kOverrideBufferSlot, nullptr};
     }
+    mLastOverrideBuffer = buffer;
+    return {kOverrideBufferSlot, buffer};
+}
+
+uint32_t HwcBufferCache::uncache(uint64_t bufferId) {
+    if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) {
+        uint32_t slot = i->second.slot;
+        mCacheByBufferId.erase(i);
+        mFreeSlots.push(slot);
+        return slot;
+    }
+    if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) {
+        mLastOverrideBuffer = nullptr;
+        return kOverrideBufferSlot;
+    }
+    return UINT32_MAX;
+}
+
+uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) {
+    Cache cache;
+    cache.slot = getLeastRecentlyUsedSlot();
+    cache.lruCounter = mLeastRecentlyUsedCounter++;
+    cache.buffer = buffer;
+    mCacheByBufferId.emplace(buffer->getId(), cache);
+    return cache.slot;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+    if (mFreeSlots.empty()) {
+        assert(!mCacheByBufferId.empty());
+        // evict the least recently used cache entry
+        auto cacheToErase = mCacheByBufferId.begin();
+        for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) {
+            if (i->second.lruCounter < cacheToErase->second.lruCounter) {
+                cacheToErase = i;
+            }
+        }
+        uint32_t slot = cacheToErase->second.slot;
+        mCacheByBufferId.erase(cacheToErase);
+        mFreeSlots.push(slot);
+    }
+    uint32_t slot = mFreeSlots.top();
+    mFreeSlots.pop();
+    return slot;
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 6631a27..426cc57 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -106,7 +106,6 @@
     dumpVal(out, "composition type", toString(compositionType), compositionType);
 
     out.append("\n      buffer: ");
-    dumpVal(out, "slot", bufferSlot);
     dumpVal(out, "buffer", buffer.get());
 
     out.append("\n      ");
@@ -122,7 +121,12 @@
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
     dumpVal(out, "dimming enabled", dimmingEnabled);
+    if (currentHdrSdrRatio > 1.01f || desiredHdrSdrRatio > 1.01f) {
+        dumpVal(out, "current hdr/sdr ratio", currentHdrSdrRatio);
+        dumpVal(out, "desired hdr/sdr ratio", desiredHdrSdrRatio);
+    }
     dumpVal(out, "colorTransform", colorTransform);
+    dumpVal(out, "caching hint", toString(cachingHint));
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index d0c5803..793959c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -29,7 +29,9 @@
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/impl/planner/Planner.h>
 #include <ftl/future.h>
+#include <gui/TraceUtils.h>
 
+#include <optional>
 #include <thread>
 
 #include "renderengine/ExternalTexture.h"
@@ -116,6 +118,10 @@
 
 void Output::setName(const std::string& name) {
     mName = name;
+    auto displayIdOpt = getDisplayId();
+    mNamePlusId = displayIdOpt ? base::StringPrintf("%s (%s)", mName.c_str(),
+                                     to_string(*displayIdOpt).c_str())
+                               : mName;
 }
 
 void Output::setCompositionEnabled(bool enabled) {
@@ -424,10 +430,11 @@
     ALOGV(__FUNCTION__);
 
     rebuildLayerStacks(refreshArgs, geomSnapshots);
+    uncacheBuffers(refreshArgs.bufferIdsToUncache);
 }
 
 void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
     ALOGV(__FUNCTION__);
 
     updateColorProfile(refreshArgs);
@@ -440,17 +447,26 @@
     GpuCompositionResult result;
     const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
     if (predictCompositionStrategy) {
-        result = prepareFrameAsync(refreshArgs);
+        result = prepareFrameAsync();
     } else {
         prepareFrame();
     }
 
     devOptRepaintFlash(refreshArgs);
-    finishFrame(refreshArgs, std::move(result));
+    finishFrame(std::move(result));
     postFramebuffer();
     renderCachedSets(refreshArgs);
 }
 
+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) {
     ATRACE_CALL();
@@ -465,6 +481,9 @@
 
     // Process the layers to determine visibility and coverage
     compositionengine::Output::CoverageState coverage{layerFESet};
+    coverage.aboveCoveredLayersExcludingOverlays = refreshArgs.hasTrustedPresentationListener
+            ? std::make_optional<Region>()
+            : std::nullopt;
     collectVisibleLayers(refreshArgs, coverage);
 
     // Compute the resulting coverage for this output, and store it for later
@@ -501,7 +520,6 @@
     // appear on multiple outputs.
     if (!coverage.latchedLayers.count(layerFE)) {
         coverage.latchedLayers.insert(layerFE);
-        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
     }
 
     // Only consider the layers on this output
@@ -520,6 +538,9 @@
         return;
     }
 
+    bool computeAboveCoveredExcludingOverlays = coverage.aboveCoveredLayersExcludingOverlays &&
+            !layerFEState->outputFilter.toInternalDisplay;
+
     /*
      * opaqueRegion: area of a surface that is fully opaque.
      */
@@ -561,6 +582,11 @@
      */
     Region shadowRegion;
 
+    /**
+     * covered region above excluding internal display overlay layers
+     */
+    std::optional<Region> coveredRegionExcludingDisplayOverlays = std::nullopt;
+
     const ui::Transform& tr = layerFEState->geomLayerTransform;
 
     // Get the visible region
@@ -633,6 +659,12 @@
     // Update accumAboveCoveredLayers for next (lower) layer
     coverage.aboveCoveredLayers.orSelf(visibleRegion);
 
+    if (CC_UNLIKELY(computeAboveCoveredExcludingOverlays)) {
+        coveredRegionExcludingDisplayOverlays =
+                coverage.aboveCoveredLayersExcludingOverlays->intersect(visibleRegion);
+        coverage.aboveCoveredLayersExcludingOverlays->orSelf(visibleRegion);
+    }
+
     // subtract the opaque region covered by the layers above us
     visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
 
@@ -719,20 +751,16 @@
             ? outputState.transform.transform(
                       transparentRegion.intersect(outputState.layerStackSpace.getContent()))
             : Region();
+    if (CC_UNLIKELY(computeAboveCoveredExcludingOverlays)) {
+        outputLayerState.coveredRegionExcludingDisplayOverlays =
+                std::move(coveredRegionExcludingDisplayOverlays);
+    }
 }
 
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
     // The base class does nothing with this call.
 }
 
-void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
-    for (auto* layer : getOutputLayersOrderedByZ()) {
-        layer->getLayerFE().prepareCompositionState(
-                args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
-                                               : LayerFE::StateSubset::Content);
-    }
-}
-
 void Output::updateCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
@@ -754,6 +782,44 @@
             forceClientComposition = false;
         }
     }
+
+    updateCompositionStateForBorder(refreshArgs);
+}
+
+void Output::updateCompositionStateForBorder(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    std::unordered_map<int32_t, const Region*> layerVisibleRegionMap;
+    // Store a map of layerId to their computed visible region.
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        int layerId = (layer->getLayerFE()).getSequence();
+        layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion);
+    }
+    OutputCompositionState& outputCompositionState = editState();
+    outputCompositionState.borderInfoList.clear();
+    bool clientComposeTopLayer = false;
+    for (const auto& borderInfo : refreshArgs.borderInfoList) {
+        renderengine::BorderRenderInfo info;
+        for (const auto& id : borderInfo.layerIds) {
+            info.combinedRegion.orSelf(*(layerVisibleRegionMap[id]));
+        }
+
+        if (!info.combinedRegion.isEmpty()) {
+            info.width = borderInfo.width;
+            info.color = borderInfo.color;
+            outputCompositionState.borderInfoList.emplace_back(std::move(info));
+            clientComposeTopLayer = true;
+        }
+    }
+
+    // In this situation we must client compose the top layer instead of using hwc
+    // because we want to draw the border above all else.
+    // This could potentially cause a bit of a performance regression if the top
+    // layer would have been rendered using hwc originally.
+    // TODO(b/227656283): Measure system's performance before enabling the border feature
+    if (clientComposeTopLayer) {
+        auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1);
+        (topLayer->editState()).forceClientComposition = true;
+    }
 }
 
 void Output::planComposition() {
@@ -776,7 +842,6 @@
     }
 
     editState().earliestPresentTime = refreshArgs.earliestPresentTime;
-    editState().previousPresentFence = refreshArgs.previousPresentFence;
     editState().expectedPresentTime = refreshArgs.expectedPresentTime;
 
     compositionengine::OutputLayer* peekThroughLayer = nullptr;
@@ -871,6 +936,13 @@
     ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
     *outHdrDataSpace = ui::Dataspace::UNKNOWN;
 
+    // An Output's layers may be stale when it is disabled. As a consequence, the layers returned by
+    // getOutputLayersOrderedByZ may not be in a valid state and it is not safe to access their
+    // properties. Return a default dataspace value in this case.
+    if (!getState().isEnabled) {
+        return ui::Dataspace::V0_SRGB;
+    }
+
     for (const auto* layer : getOutputLayersOrderedByZ()) {
         switch (layer->getLayerFE().getCompositionState()->dataspace) {
             case ui::Dataspace::V0_SCRGB:
@@ -1019,7 +1091,7 @@
             [&, changes]() { return chooseCompositionStrategy(changes); });
 }
 
-GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+GpuCompositionResult Output::prepareFrameAsync() {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
     auto& state = editState();
@@ -1039,7 +1111,7 @@
     GpuCompositionResult compositionResult;
     if (dequeueSucceeded) {
         std::optional<base::unique_fd> optFd =
-                composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+                composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
         if (optFd) {
             compositionResult.fence = std::move(*optFd);
         }
@@ -1077,7 +1149,7 @@
             std::shared_ptr<renderengine::ExternalTexture> buffer;
             updateProtectedContentState();
             dequeueRenderBuffer(&bufferFence, &buffer);
-            static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
+            static_cast<void>(composeSurfaces(dirtyRegion, buffer, bufferFence));
             mRenderSurface->queueBuffer(base::unique_fd());
         }
     }
@@ -1089,7 +1161,7 @@
     prepareFrame();
 }
 
-void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
+void Output::finishFrame(GpuCompositionResult&& result) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
     const auto& outputState = getState();
@@ -1114,7 +1186,7 @@
         }
         // Repaint the framebuffer (if needed), getting the optional fence for when
         // the composition completes.
-        optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+        optReadyFence = composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
     }
     if (!optReadyFence) {
         return;
@@ -1122,7 +1194,8 @@
 
     if (isPowerHintSessionEnabled()) {
         // get fence end time to know when gpu is complete in display
-        setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get()))));
+        setHintSessionGpuFence(
+                std::make_unique<FenceTime>(sp<Fence>::make(dup(optReadyFence->get()))));
     }
     // swap buffers (presentation)
     mRenderSurface->queueBuffer(std::move(*optReadyFence));
@@ -1141,15 +1214,9 @@
         bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
             return layer->getLayerFE().getCompositionState()->hasProtectedContent;
         });
-        if (needsProtected != renderEngine.isProtected()) {
-            renderEngine.useProtectedContext(needsProtected);
-        }
-        if (needsProtected != mRenderSurface->isProtected() &&
-            needsProtected == renderEngine.isProtected()) {
+        if (needsProtected != mRenderSurface->isProtected()) {
             mRenderSurface->setProtected(needsProtected);
         }
-    } else if (!outputState.isSecure && renderEngine.isProtected()) {
-        renderEngine.useProtectedContext(false);
     }
 }
 
@@ -1173,14 +1240,15 @@
 }
 
 std::optional<base::unique_fd> Output::composeSurfaces(
-        const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
-        std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+        const Region& debugRegion, std::shared_ptr<renderengine::ExternalTexture> tex,
+        base::unique_fd& fd) {
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
     const auto& outputState = getState();
-    const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
-                                                      outputState.usesClientComposition};
+    const TracedOrdinal<bool> hasClientComposition = {
+        base::StringPrintf("hasClientComposition %s", mNamePlusId.c_str()),
+        outputState.usesClientComposition};
     if (!hasClientComposition) {
         setExpensiveRenderingExpected(false);
         return base::unique_fd();
@@ -1195,33 +1263,8 @@
 
     ALOGV("hasClientComposition");
 
-    renderengine::DisplaySettings clientCompositionDisplay;
-    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
-    clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
-    clientCompositionDisplay.orientation =
-            ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
-    clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
-            ? outputState.dataspace
-            : ui::Dataspace::UNKNOWN;
-
-    // If we have a valid current display brightness use that, otherwise fall back to the
-    // display's max desired
-    clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f
-            ? outputState.displayBrightnessNits
-            : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
-    clientCompositionDisplay.maxLuminance =
-            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
-    clientCompositionDisplay.targetLuminanceNits =
-            outputState.clientTargetBrightness * outputState.displayBrightnessNits;
-    clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage;
-    clientCompositionDisplay.renderIntent =
-            static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(
-                    outputState.renderIntent);
-
-    // Compute the global color transform matrix.
-    clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
-    clientCompositionDisplay.deviceHandlesColorTransform =
-            outputState.usesDeviceComposition || getSkipColorTransform();
+    renderengine::DisplaySettings clientCompositionDisplay =
+            generateClientCompositionDisplaySettings();
 
     // Generate the client composition requests for the layers on this output.
     auto& renderEngine = getCompositionEngine().getRenderEngine();
@@ -1255,9 +1298,7 @@
     // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
     // GPU composition can finish in time. We must reset GPU frequency afterwards,
     // because high frequency consumes extra battery.
-    const bool expensiveBlurs =
-            refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
-    const bool expensiveRenderingExpected = expensiveBlurs ||
+    const bool expensiveRenderingExpected =
             std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
                         [outputDataspace =
                                  clientCompositionDisplay.outputDataspace](const auto& layer) {
@@ -1284,11 +1325,10 @@
     // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
     const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
 
-    auto fenceResult =
-            toFenceResult(renderEngine
-                                  .drawLayers(clientCompositionDisplay, clientRenderEngineLayers,
-                                              tex, useFramebufferCache, std::move(fd))
-                                  .get());
+    auto fenceResult = renderEngine
+                               .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex,
+                                           useFramebufferCache, std::move(fd))
+                               .get();
 
     if (mClientCompositionRequestCache && fenceStatus(fenceResult) != NO_ERROR) {
         // If rendering was not successful, remove the request from the cache.
@@ -1297,10 +1337,13 @@
 
     const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
 
-    if (auto& timeStats = getCompositionEngine().getTimeStats(); fence->isValid()) {
-        timeStats.recordRenderEngineDuration(renderEngineStart, std::make_shared<FenceTime>(fence));
-    } else {
-        timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+    if (auto timeStats = getCompositionEngine().getTimeStats()) {
+        if (fence->isValid()) {
+            timeStats->recordRenderEngineDuration(renderEngineStart,
+                                                  std::make_shared<FenceTime>(fence));
+        } else {
+            timeStats->recordRenderEngineDuration(renderEngineStart, systemTime());
+        }
     }
 
     for (auto* clientComposedLayer : clientCompositionLayersFE) {
@@ -1310,6 +1353,47 @@
     return base::unique_fd(fence->dup());
 }
 
+renderengine::DisplaySettings Output::generateClientCompositionDisplaySettings() const {
+    const auto& outputState = getState();
+
+    renderengine::DisplaySettings clientCompositionDisplay;
+    clientCompositionDisplay.namePlusId = mNamePlusId;
+    clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+    clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
+    clientCompositionDisplay.orientation =
+            ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
+    clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
+            ? outputState.dataspace
+            : ui::Dataspace::UNKNOWN;
+
+    // If we have a valid current display brightness use that, otherwise fall back to the
+    // display's max desired
+    clientCompositionDisplay.currentLuminanceNits = outputState.displayBrightnessNits > 0.f
+            ? outputState.displayBrightnessNits
+            : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+    clientCompositionDisplay.maxLuminance =
+            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+    clientCompositionDisplay.targetLuminanceNits =
+            outputState.clientTargetBrightness * outputState.displayBrightnessNits;
+    clientCompositionDisplay.dimmingStage = outputState.clientTargetDimmingStage;
+    clientCompositionDisplay.renderIntent =
+            static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(
+                    outputState.renderIntent);
+
+    // Compute the global color transform matrix.
+    clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+    for (auto& info : outputState.borderInfoList) {
+        renderengine::BorderRenderInfo borderInfo;
+        borderInfo.width = info.width;
+        borderInfo.color = info.color;
+        borderInfo.combinedRegion = info.combinedRegion;
+        clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
+    }
+    clientCompositionDisplay.deviceHandlesColorTransform =
+            outputState.usesDeviceComposition || getSkipColorTransform();
+    return clientCompositionDisplay;
+}
+
 std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
       bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) {
     std::vector<LayerFE::LayerSettings> clientCompositionLayers;
@@ -1320,7 +1404,7 @@
     bool firstLayer = true;
 
     bool disableBlurs = false;
-    sp<GraphicBuffer> previousOverrideBuffer = nullptr;
+    uint64_t previousOverrideBufferId = 0;
 
     for (auto* layer : getOutputLayersOrderedByZ()) {
         const auto& layerState = layer->getState();
@@ -1356,11 +1440,10 @@
                 !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
 
         if (clientComposition || clearClientComposition) {
-            std::vector<LayerFE::LayerSettings> results;
-            if (layer->getState().overrideInfo.buffer != nullptr) {
-                if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
-                    results = layer->getOverrideCompositionList();
-                    previousOverrideBuffer = layer->getState().overrideInfo.buffer->getBuffer();
+            if (auto overrideSettings = layer->getOverrideCompositionSettings()) {
+                if (overrideSettings->bufferId != previousOverrideBufferId) {
+                    previousOverrideBufferId = overrideSettings->bufferId;
+                    clientCompositionLayers.push_back(std::move(*overrideSettings));
                     ALOGV("Replacing [%s] with override in RE", layer->getLayerFE().getDebugName());
                 } else {
                     ALOGV("Skipping redundant override buffer for [%s] in RE",
@@ -1385,21 +1468,20 @@
                                        .realContentIsVisible = realContentIsVisible,
                                        .clearContent = !clientComposition,
                                        .blurSetting = blurSetting,
-                                       .whitePointNits = layerState.whitePointNits};
-                results = layerFE.prepareClientCompositionList(targetSettings);
-                if (realContentIsVisible && !results.empty()) {
-                    layer->editState().clientCompositionTimestamp = systemTime();
+                                       .whitePointNits = layerState.whitePointNits,
+                                       .treat170mAsSrgb = outputState.treat170mAsSrgb};
+                if (auto clientCompositionSettings =
+                            layerFE.prepareClientComposition(targetSettings)) {
+                    clientCompositionLayers.push_back(std::move(*clientCompositionSettings));
+                    if (realContentIsVisible) {
+                        layer->editState().clientCompositionTimestamp = systemTime();
+                    }
                 }
             }
 
             if (clientComposition) {
                 outLayerFEs.push_back(&layerFE);
             }
-
-            clientCompositionLayers.insert(clientCompositionLayers.end(),
-                                           std::make_move_iterator(results.begin()),
-                                           std::make_move_iterator(results.end()));
-            results.clear();
         }
 
         firstLayer = false;
@@ -1438,7 +1520,7 @@
 }
 
 void Output::postFramebuffer() {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
     ALOGV(__FUNCTION__);
 
     if (!getState().isEnabled) {
@@ -1447,7 +1529,6 @@
 
     auto& outputState = editState();
     outputState.dirtyRegion.clear();
-    mRenderSurface->flip();
 
     auto frame = presentAndGetFrameFences();
 
@@ -1475,8 +1556,9 @@
             releaseFence =
                     Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
         }
-        layer->getLayerFE().onLayerDisplayed(
-                ftl::yield<FenceResult>(std::move(releaseFence)).share());
+        layer->getLayerFE()
+                .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
+                                  outputState.layerFilter.layerStack);
     }
 
     // We've got a list of layers needing fences, that are disjoint with
@@ -1484,7 +1566,8 @@
     // supply them with the present fence.
     for (auto& weakLayer : mReleasedLayers) {
         if (const auto layer = weakLayer.promote()) {
-            layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share());
+            layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
+                                    outputState.layerFilter.layerStack);
         }
     }
 
@@ -1493,9 +1576,10 @@
 }
 
 void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
-    if (mPlanner) {
-        mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime,
-                                   getState().usesDeviceComposition || getSkipColorTransform());
+    const auto& outputState = getState();
+    if (mPlanner && outputState.isEnabled) {
+        mPlanner->renderCachedSets(outputState, refreshArgs.scheduledFrameTime,
+                                   outputState.usesDeviceComposition || getSkipColorTransform());
     }
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 948c0c9..c512a1e 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -62,14 +62,18 @@
     dumpVal(out, "sdrWhitePointNits", sdrWhitePointNits);
     dumpVal(out, "clientTargetBrightness", clientTargetBrightness);
     dumpVal(out, "displayBrightness", displayBrightness);
-
     out.append("\n   ");
     dumpVal(out, "compositionStrategyPredictionState", ftl::enum_string(strategyPrediction));
+    out.append("\n   ");
 
     out.append("\n   ");
     dumpVal(out, "treate170mAsSrgb", treat170mAsSrgb);
 
-    out += '\n';
+    out.append("\n");
+    for (const auto& borderRenderInfo : borderInfoList) {
+        dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion);
+    }
+    out.append("\n");
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 1bb9d0eb..0ac0ecb 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -340,10 +340,17 @@
         state.dimmingRatio = 1.f;
         state.whitePointNits = getOutput().getState().displayBrightnessNits;
     } else {
-        state.dimmingRatio = std::clamp(getOutput().getState().sdrWhitePointNits /
-                                                getOutput().getState().displayBrightnessNits,
-                                        0.f, 1.f);
-        state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+        float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
+        // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
+        // range that we may need to re-adjust to the current display conditions
+        if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
+            layerFEState->currentHdrSdrRatio > 1.01f) {
+            layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
+        }
+        state.dimmingRatio =
+                std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
+                           1.f);
+        state.whitePointNits = layerBrightnessNits;
     }
 
     // These are evaluated every frame as they can potentially change at any
@@ -578,6 +585,7 @@
         case Composition::CURSOR:
         case Composition::DEVICE:
         case Composition::DISPLAY_DECORATION:
+        case Composition::REFRESH_RATE_INDICATOR:
             writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
             break;
         case Composition::INVALID:
@@ -610,6 +618,40 @@
     }
 }
 
+void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) {
+    auto& state = editState();
+    // Skip doing this if there is no HWC interface
+    if (!state.hwc) {
+        return;
+    }
+
+    // Uncache the active buffer last so that it's the first buffer to be purged from the cache
+    // next time a buffer is sent to this layer.
+    bool uncacheActiveBuffer = false;
+
+    std::vector<uint32_t> slotsToClear;
+    for (uint64_t bufferId : bufferIdsToUncache) {
+        if (bufferId == state.hwc->activeBufferId) {
+            uncacheActiveBuffer = true;
+        } else {
+            uint32_t slot = state.hwc->hwcBufferCache.uncache(bufferId);
+            if (slot != UINT32_MAX) {
+                slotsToClear.push_back(slot);
+            }
+        }
+    }
+    if (uncacheActiveBuffer) {
+        slotsToClear.push_back(state.hwc->hwcBufferCache.uncache(state.hwc->activeBufferId));
+    }
+
+    hal::Error error =
+            state.hwc->hwcLayer->setBufferSlotsToClear(slotsToClear, state.hwc->activeBufferSlot);
+    if (error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to clear buffer slots: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
 void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
                                         const LayerFECompositionState& outputIndependentState,
                                         bool skipLayer) {
@@ -622,27 +664,37 @@
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
 
-    sp<GraphicBuffer> buffer = outputIndependentState.buffer;
-    sp<Fence> acquireFence = outputIndependentState.acquireFence;
-    int slot = outputIndependentState.bufferSlot;
-    if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
-        buffer = getState().overrideInfo.buffer->getBuffer();
-        acquireFence = getState().overrideInfo.acquireFence;
-        slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
+    HwcSlotAndBuffer hwcSlotAndBuffer;
+    sp<Fence> hwcFence;
+    {
+        // Editing the state only because we update the HWC buffer cache and active buffer.
+        auto& state = editState();
+        // Override buffers use a special cache slot so that they don't evict client buffers.
+        if (state.overrideInfo.buffer != nullptr && !skipLayer) {
+            hwcSlotAndBuffer = state.hwc->hwcBufferCache.getOverrideHwcSlotAndBuffer(
+                    state.overrideInfo.buffer->getBuffer());
+            hwcFence = state.overrideInfo.acquireFence;
+            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
+            // slot will be used first, allowing the memory to be freed as soon as possible.
+            state.hwc->activeBufferId = state.overrideInfo.buffer->getBuffer()->getId();
+        } else {
+            hwcSlotAndBuffer =
+                    state.hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+            hwcFence = outputIndependentState.acquireFence;
+            // Keep track of the active buffer ID so when it's discarded we uncache it last so its
+            // slot will be used first, allowing the memory to be freed as soon as possible.
+            state.hwc->activeBufferId = outputIndependentState.buffer->getId();
+        }
+        // Keep track of the active buffer slot, so we can restore it after clearing other buffer
+        // slots.
+        state.hwc->activeBufferSlot = hwcSlotAndBuffer.slot;
     }
 
-    ALOGV("Writing buffer %p", buffer.get());
-
-    uint32_t hwcSlot = 0;
-    sp<GraphicBuffer> hwcBuffer;
-    // We need access to the output-dependent state for the buffer cache there,
-    // though otherwise the buffer is not output-dependent.
-    editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
-
-    if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+    if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
         error != hal::Error::NONE) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
-              to_string(error).c_str(), static_cast<int32_t>(error));
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+              hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
     }
 }
 
@@ -729,6 +781,7 @@
         case Composition::CURSOR:
         case Composition::SIDEBAND:
         case Composition::DISPLAY_DECORATION:
+        case Composition::REFRESH_RATE_INDICATOR:
             result = (to == Composition::CLIENT || to == Composition::DEVICE);
             break;
     }
@@ -787,7 +840,7 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-std::vector<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionList() const {
+std::optional<LayerFE::LayerSettings> OutputLayer::getOverrideCompositionSettings() const {
     if (getState().overrideInfo.buffer == nullptr) {
         return {};
     }
@@ -816,7 +869,7 @@
     settings.alpha = 1.0f;
     settings.whitePointNits = getOutput().getState().sdrWhitePointNits;
 
-    return {static_cast<LayerFE::LayerSettings>(settings)};
+    return settings;
 }
 
 void OutputLayer::dump(std::string& out) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 5a3af7b..0fe55db 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -251,10 +251,6 @@
     mDisplaySurface->onFrameCommitted();
 }
 
-void RenderSurface::flip() {
-    mPageFlipCount++;
-}
-
 void RenderSurface::dump(std::string& out) const {
     using android::base::StringAppendF;
 
@@ -265,7 +261,6 @@
     dumpVal(out, "size", mSize);
     StringAppendF(&out, "ANativeWindow=%p (format %d) ", mNativeWindow.get(),
                   ANativeWindow_getFormat(mNativeWindow.get()));
-    dumpVal(out, "flips", mPageFlipCount);
     out.append("\n");
 
     String8 surfaceDump;
@@ -273,14 +268,6 @@
     out.append(surfaceDump);
 }
 
-std::uint32_t RenderSurface::getPageFlipCount() const {
-    return mPageFlipCount;
-}
-
-void RenderSurface::setPageFlipCountForTest(std::uint32_t count) {
-    mPageFlipCount = count;
-}
-
 void RenderSurface::setSizeForTest(const ui::Size& size) {
     mSize = size;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index d6f02ee..8ced0ac 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -193,11 +193,11 @@
     std::vector<renderengine::LayerSettings> layerSettings;
     renderengine::LayerSettings highlight;
     for (const auto& layer : mLayers) {
-        const auto clientCompositionList =
-                layer.getState()->getOutputLayer()->getLayerFE().prepareClientCompositionList(
-                        targetSettings);
-        layerSettings.insert(layerSettings.end(), clientCompositionList.cbegin(),
-                             clientCompositionList.cend());
+        if (auto clientCompositionSettings =
+                    layer.getState()->getOutputLayer()->getLayerFE().prepareClientComposition(
+                            targetSettings)) {
+            layerSettings.push_back(std::move(*clientCompositionSettings));
+        }
     }
 
     renderengine::LayerSettings blurLayerSettings;
@@ -205,43 +205,40 @@
         auto blurSettings = targetSettings;
         blurSettings.blurSetting =
                 LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
-        auto clientCompositionList =
-                mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
-                        blurSettings);
-        blurLayerSettings = clientCompositionList.back();
+
+        auto blurLayerSettings =
+                mBlurLayer->getOutputLayer()->getLayerFE().prepareClientComposition(blurSettings);
         // This mimics Layer::prepareClearClientComposition
-        blurLayerSettings.skipContentDraw = true;
-        blurLayerSettings.name = std::string("blur layer");
+        blurLayerSettings->skipContentDraw = true;
+        blurLayerSettings->name = std::string("blur layer");
         // Clear out the shadow settings
-        blurLayerSettings.shadow = {};
-        layerSettings.push_back(blurLayerSettings);
+        blurLayerSettings->shadow = {};
+        layerSettings.push_back(std::move(*blurLayerSettings));
     }
 
-    renderengine::LayerSettings holePunchSettings;
-    renderengine::LayerSettings holePunchBackgroundSettings;
     if (mHolePunchLayer) {
         auto& layerFE = mHolePunchLayer->getOutputLayer()->getLayerFE();
-        auto clientCompositionList = layerFE.prepareClientCompositionList(targetSettings);
-        // Assume that the final layer contains the buffer that we want to
-        // replace with a hole punch.
-        holePunchSettings = clientCompositionList.back();
+
+        auto holePunchSettings = layerFE.prepareClientComposition(targetSettings);
         // This mimics Layer::prepareClearClientComposition
-        holePunchSettings.source.buffer.buffer = nullptr;
-        holePunchSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
-        holePunchSettings.disableBlending = true;
-        holePunchSettings.alpha = 0.0f;
-        holePunchSettings.name =
+        holePunchSettings->source.buffer.buffer = nullptr;
+        holePunchSettings->source.solidColor = half3(0.0f, 0.0f, 0.0f);
+        holePunchSettings->disableBlending = true;
+        holePunchSettings->alpha = 0.0f;
+        holePunchSettings->name =
                 android::base::StringPrintf("hole punch layer for %s", layerFE.getDebugName());
-        layerSettings.push_back(holePunchSettings);
 
         // Add a solid background as the first layer in case there is no opaque
         // buffer behind the punch hole
+        renderengine::LayerSettings holePunchBackgroundSettings;
         holePunchBackgroundSettings.alpha = 1.0f;
         holePunchBackgroundSettings.name = std::string("holePunchBackground");
-        holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
+        holePunchBackgroundSettings.geometry.boundaries = holePunchSettings->geometry.boundaries;
         holePunchBackgroundSettings.geometry.positionTransform =
-                holePunchSettings.geometry.positionTransform;
-        layerSettings.emplace(layerSettings.begin(), holePunchBackgroundSettings);
+                holePunchSettings->geometry.positionTransform;
+        layerSettings.emplace(layerSettings.begin(), std::move(holePunchBackgroundSettings));
+
+        layerSettings.push_back(std::move(*holePunchSettings));
     }
 
     if (sDebugHighlighLayers) {
@@ -276,11 +273,10 @@
 
     constexpr bool kUseFramebufferCache = false;
 
-    auto fenceResult =
-            toFenceResult(renderEngine
-                                  .drawLayers(displaySettings, layerSettings, texture->get(),
-                                              kUseFramebufferCache, std::move(bufferFence))
-                                  .get());
+    auto fenceResult = renderEngine
+                               .drawLayers(displaySettings, layerSettings, texture->get(),
+                                           kUseFramebufferCache, std::move(bufferFence))
+                               .get();
 
     if (fenceStatus(fenceResult) == NO_ERROR) {
         mDrawFence = std::move(fenceResult).value_or(Fence::NO_FENCE);
@@ -382,6 +378,10 @@
             // to avoid flickering/color differences.
             return true;
         }
+        // TODO(b/274804887): temp fix of overdimming issue, skip caching if hsdr/sdr ratio > 1.01f
+        if (layer.getState()->getHdrSdrRatio() > 1.01f) {
+            return true;
+        }
         return false;
     });
 }
@@ -397,6 +397,18 @@
     });
 }
 
+bool CachedSet::cachingHintExcludesLayers() const {
+    const bool shouldExcludeLayers =
+            std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
+                return layer.getState()->getCachingHint() == gui::CachingHint::Disabled;
+            });
+
+    LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1,
+                        "CachedSet is invalid: should be excluded but contains %zu layers",
+                        getLayerCount());
+    return shouldExcludeLayers;
+}
+
 void CachedSet::dump(std::string& result) const {
     const auto now = std::chrono::steady_clock::now();
 
@@ -415,8 +427,8 @@
 
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
-        if (auto* buffer = mLayers[0].getBuffer().get()) {
-            base::StringAppendF(&result, "    Buffer %p", buffer);
+        if (const sp<GraphicBuffer> buffer = mLayers[0].getState()->getBuffer().promote()) {
+            base::StringAppendF(&result, "    Buffer %p", buffer.get());
             base::StringAppendF(&result, "    Format %s",
                                 decodePixelFormat(buffer->getPixelFormat()).c_str());
         }
@@ -426,8 +438,8 @@
         result.append("    Cached set of:\n");
         for (const Layer& layer : mLayers) {
             base::StringAppendF(&result, "      Layer [%s]\n", layer.getName().c_str());
-            if (auto* buffer = layer.getBuffer().get()) {
-                base::StringAppendF(&result, "       Buffer %p", buffer);
+            if (const sp<GraphicBuffer> buffer = layer.getState()->getBuffer().promote()) {
+                base::StringAppendF(&result, "       Buffer %p", buffer.get());
                 base::StringAppendF(&result, "    Format[%s]",
                                     decodePixelFormat(buffer->getPixelFormat()).c_str());
             }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 9175dd0..13b6307 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -413,6 +413,7 @@
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
         bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
+        const bool layerDeniedFromCaching = currentSet->cachingHintExcludesLayers();
 
         // Layers should also be considered inactive whenever their framerate is lower than 1fps.
         if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
@@ -424,7 +425,8 @@
             }
         }
 
-        if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
+        if (!layerDeniedFromCaching && layerIsInactive &&
+            (firstLayer || runHasFirstLayer || !layerHasBlur) &&
             !currentSet->hasUnsupportedDataspace()) {
             if (isPartOfRun) {
                 builder.increment();
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 2fc029f..6064126 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -151,6 +151,10 @@
                 // A for "Alpha", since the decoration is an alpha layer.
                 result.append("A");
                 break;
+            case aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR:
+                // R for "Refresh", since the layer is Refresh rate overlay.
+                result.append("R");
+                break;
         }
     }
     return result;
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index de9de01..60ed660 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -62,17 +62,16 @@
 }
 
 TEST_F(CompositionEngineTest, canSetRenderEngine) {
-    renderengine::mock::RenderEngine* renderEngine =
-            new StrictMock<renderengine::mock::RenderEngine>();
-    mEngine.setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(renderEngine));
+    auto renderEngine = std::make_unique<StrictMock<renderengine::mock::RenderEngine>>();
+    mEngine.setRenderEngine(renderEngine.get());
 
-    EXPECT_EQ(renderEngine, &mEngine.getRenderEngine());
+    EXPECT_EQ(renderEngine.get(), &mEngine.getRenderEngine());
 }
 
 TEST_F(CompositionEngineTest, canSetTimeStats) {
     mEngine.setTimeStats(mTimeStats);
 
-    EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+    EXPECT_EQ(mTimeStats.get(), mEngine.getTimeStats());
 }
 
 /*
@@ -108,12 +107,6 @@
     EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
     EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
 
-    // The next step in presenting is to make sure all outputs have the latest
-    // state from the front-end (SurfaceFlinger).
-    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
-    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
-
     // The last step is to actually present each output.
     EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
     EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
@@ -175,21 +168,18 @@
     {
         InSequence seq;
         EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
     }
 
     {
         InSequence seq;
         EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
-        EXPECT_CALL(*mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
         EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
     }
 
@@ -222,9 +212,12 @@
     nsecs_t ts1 = 0;
     nsecs_t ts2 = 0;
     nsecs_t ts3 = 0;
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _))
+            .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -238,9 +231,9 @@
 }
 
 TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mEngine.setNeedsAnotherUpdateForTest(true);
 
@@ -255,9 +248,9 @@
 
 TEST_F(CompositionTestPreComposition,
        preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
-    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
-    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
 
     mRefreshArgs.outputs = {mOutput1};
     mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 5369642..9be6bc2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -194,7 +194,7 @@
     StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+    sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make();
 };
 
 struct PartialMockDisplayTestCommon : public DisplayTestCommon {
@@ -524,7 +524,7 @@
     auto args = getDisplayCreationArgsForGpuVirtualDisplay();
     std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
 
-    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layerXLayerFE = sp<StrictMock<mock::LayerFE>>::make();
 
     {
         Output::ReleasedLayers releasedLayers;
@@ -542,7 +542,7 @@
 }
 
 TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) {
-    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> layerXLayerFE = sp<StrictMock<mock::LayerFE>>::make();
 
     {
         Output::ReleasedLayers releasedLayers;
@@ -558,7 +558,7 @@
 }
 
 TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) {
-    sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
+    sp<mock::LayerFE> unknownLayer = sp<StrictMock<mock::LayerFE>>::make();
 
     CompositionRefreshArgs refreshArgs;
     refreshArgs.layersWithQueuedFrames.push_back(mLayer1.layerFE);
@@ -595,7 +595,7 @@
 TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
     EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
             .WillOnce(Return(INVALID_OPERATION));
 
     chooseCompositionStrategy(mDisplay.get());
@@ -619,8 +619,8 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
-            .WillOnce(testing::DoAll(testing::SetArgPointee<5>(mDeviceRequestedChanges),
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
+            .WillOnce(testing::DoAll(testing::SetArgPointee<4>(mDeviceRequestedChanges),
                                      Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
             .Times(1);
@@ -672,8 +672,8 @@
             .WillOnce(Return(false));
 
     EXPECT_CALL(mHwComposer,
-                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
-            .WillOnce(DoAll(SetArgPointee<5>(mDeviceRequestedChanges), Return(NO_ERROR)));
+                getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
+            .WillOnce(DoAll(SetArgPointee<4>(mDeviceRequestedChanges), Return(NO_ERROR)));
     EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(mDeviceRequestedChanges.changedTypes))
             .Times(1);
     EXPECT_CALL(*mDisplay, applyDisplayRequests(mDeviceRequestedChanges.displayRequests)).Times(1);
@@ -897,11 +897,11 @@
 }
 
 TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) {
-    sp<Fence> presentFence = new Fence();
-    sp<Fence> layer1Fence = new Fence();
-    sp<Fence> layer2Fence = new Fence();
+    sp<Fence> presentFence = sp<Fence>::make();
+    sp<Fence> layer1Fence = sp<Fence>::make();
+    sp<Fence> layer2Fence = sp<Fence>::make();
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _, _))
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _))
             .Times(1);
     EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID)))
             .WillOnce(Return(presentFence));
@@ -959,7 +959,7 @@
     mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
     mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
 
-    mDisplay->finishFrame({}, std::move(mResultWithBuffer));
+    mDisplay->finishFrame(std::move(mResultWithBuffer));
 }
 
 TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -980,7 +980,7 @@
     gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
 
     gpuDisplay->beginFrame();
-    gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+    gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
 }
 
 TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
@@ -1001,7 +1001,7 @@
     gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
 
     gpuDisplay->beginFrame();
-    gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+    gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
 }
 
 TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
@@ -1022,7 +1022,7 @@
     gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
 
     gpuDisplay->beginFrame();
-    gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
+    gpuDisplay->finishFrame(std::move(mResultWithBuffer));
 }
 
 /*
@@ -1046,8 +1046,8 @@
     NiceMock<android::mock::HWComposer> mHwComposer;
     NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
     NiceMock<mock::CompositionEngine> mCompositionEngine;
-    sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
-    sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+    sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make();
+    sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make();
     std::shared_ptr<Display> mDisplay;
     impl::RenderSurface* mRenderSurface;
 
@@ -1078,7 +1078,7 @@
 
     mDisplay->editState().isEnabled = true;
 
-    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _, _));
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
     EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
 
     mDisplay->postFramebuffer();
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index 00eafb1..c5fb594 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,64 +22,172 @@
 namespace android::compositionengine {
 namespace {
 
-class TestableHwcBufferCache : public impl::HwcBufferCache {
-public:
-    void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
-                      sp<GraphicBuffer>* outBuffer) {
-        HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
-    }
-};
+using impl::HwcBufferCache;
+using impl::HwcSlotAndBuffer;
 
 class HwcBufferCacheTest : public testing::Test {
 public:
     ~HwcBufferCacheTest() override = default;
 
-    void testSlot(const int inSlot, const uint32_t expectedSlot) {
-        uint32_t outSlot;
-        sp<GraphicBuffer> outBuffer;
-
-        // The first time, the output  is the same as the input
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer1, outBuffer);
-
-        // The second time with the same buffer, the outBuffer is nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // With a new buffer, the outBuffer is the input.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(mBuffer2, outBuffer);
-
-        // Again, the second request with the same buffer sets outBuffer to nullptr.
-        mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-
-        // Setting a slot to use nullptr lookslike works, but note that
-        // the output values make it look like no new buffer is being set....
-        mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
-        EXPECT_EQ(expectedSlot, outSlot);
-        EXPECT_EQ(nullptr, outBuffer.get());
-    }
-
-    impl::HwcBufferCache mCache;
-    sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-    sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+    sp<GraphicBuffer> mBuffer1 =
+            sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+    sp<GraphicBuffer> mBuffer2 =
+            sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
 };
 
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
-    testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+    EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+    EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1);
+
+    HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+    EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot);
+    EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+    EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2);
 }
 
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
-    testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+    EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+    EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+    HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+    EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+    EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
 }
 
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
-    testSlot(-123, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    sp<GraphicBuffer> graphicBuffers[100];
+    HwcSlotAndBuffer slotsAndBuffers[100];
+    int finalCachedBufferIndex = 0;
+    for (int i = 0; i < 100; ++i) {
+        graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+        slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+        // we fill up the cache when the slot number for the first buffer is reused
+        if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+            finalCachedBufferIndex = i;
+            break;
+        }
+    }
+    ASSERT_GT(finalCachedBufferIndex, 1);
+    // the final cached buffer has the same slot value as the oldest buffer
+    EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot);
+    // the oldest buffer is no longer in the cache because it was evicted
+    EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+    ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+    HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+    ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+
+    // the 1st buffer should be found in the cache with a slot number
+    EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot);
+    // since the 1st buffer has been previously uncached, we should no longer receive a slot number
+    EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+    // the 2nd buffer should be still found in the cache with a slot number
+    EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot);
+    // since the 2nd buffer has been previously uncached, we should no longer receive a slot number
+    EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+    ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+    EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenCached_returnsSameSlotAndNullBuffer) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer originalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+    EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+    EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+    HwcSlotAndBuffer finalSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+    EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+    EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
+}
+
+TEST_F(HwcBufferCacheTest, getOverrideHwcSlotAndBuffer_whenSlotsFull_returnsIndependentSlot) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    sp<GraphicBuffer> graphicBuffers[100];
+    HwcSlotAndBuffer slotsAndBuffers[100];
+    int finalCachedBufferIndex = -1;
+    for (int i = 0; i < 100; ++i) {
+        graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+        slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+        // we fill up the cache when the slot number for the first buffer is reused
+        if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+            finalCachedBufferIndex = i;
+            break;
+        }
+    }
+    // expect to have cached at least a few buffers before evicting
+    ASSERT_GT(finalCachedBufferIndex, 1);
+
+    sp<GraphicBuffer> overrideBuffer =
+            sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+    HwcSlotAndBuffer overrideSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(overrideBuffer);
+    // expect us to have a slot number
+    EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
+    // expect this to be the first time we cached the buffer
+    EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr);
+
+    // expect the slot number to not equal any other slot number, even after the slots have been
+    // exhausted, indicating that the override buffer slot is independent from the slots for
+    // non-override buffers
+    for (int i = 0; i < finalCachedBufferIndex; ++i) {
+        EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot);
+    }
+    // the override buffer is independently uncached from the oldest cached buffer
+    // expect to find the override buffer still in the override buffer slot
+    EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot);
+    // expect that the first buffer was not evicted from the cache when the override buffer was
+    // cached
+    EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+    ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+    EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
+    EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) {
+    HwcBufferCache cache;
+    sp<GraphicBuffer> outBuffer;
+
+    HwcSlotAndBuffer hwcSlotAndBuffer = cache.getOverrideHwcSlotAndBuffer(mBuffer1);
+    ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+    EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index d933b94..b0b1a02 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -56,6 +56,7 @@
     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));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index d7704a8..67b94ee 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -54,22 +54,21 @@
     MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
 
     MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
-    MOCK_METHOD6(getDeviceCompositionChanges,
-                 status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
-                          const std::shared_ptr<FenceTime>&, nsecs_t,
-                          std::optional<android::HWComposer::DeviceRequestedChanges>*));
+    MOCK_METHOD5(getDeviceCompositionChanges,
+                 status_t(HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>,
+                          nsecs_t, std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
-    MOCK_METHOD3(presentAndGetReleaseFences,
-                 status_t(HalDisplayId, std::chrono::steady_clock::time_point,
-                          const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD2(presentAndGetReleaseFences,
+                 status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
     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>&));
@@ -92,7 +91,7 @@
     MOCK_METHOD2(onHotplug,
                  std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
     MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
-    MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
+    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_METHOD1(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId));
@@ -111,6 +110,12 @@
     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));
@@ -138,6 +143,9 @@
     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));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index c8bd5e4..961ec80 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -34,37 +34,35 @@
     MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
                 (override));
     MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
-    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
-    MOCK_METHOD(void, sendActualWorkDuration, (), (override));
-    MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
-    MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
+    MOCK_METHOD(void, reportActualWorkDuration, (), (override));
+    MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
     MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
     MOCK_METHOD(void, setGpuFenceTime,
                 (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
     MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
     MOCK_METHOD(void, setRequiresClientComposition,
                 (DisplayId displayId, bool requiresClientComposition), (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId,
-                 std::chrono::steady_clock::time_point earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+                (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, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 5290bd9..aa83883 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -42,6 +42,8 @@
 
 using testing::_;
 using testing::InSequence;
+using testing::Mock;
+using testing::NiceMock;
 using testing::Return;
 using testing::ReturnRef;
 using testing::StrictMock;
@@ -82,13 +84,13 @@
 
 struct OutputLayerTest : public testing::Test {
     struct OutputLayer final : public impl::OutputLayer {
-        OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
+        OutputLayer(const compositionengine::Output& output, compositionengine::LayerFE& layerFE)
               : mOutput(output), mLayerFE(layerFE) {}
         ~OutputLayer() override = default;
 
         // compositionengine::OutputLayer overrides
         const compositionengine::Output& getOutput() const override { return mOutput; }
-        compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+        compositionengine::LayerFE& getLayerFE() const override { return mLayerFE; }
         const impl::OutputLayerCompositionState& getState() const override { return mState; }
         impl::OutputLayerCompositionState& editState() override { return mState; }
 
@@ -96,21 +98,22 @@
         void dumpState(std::string& out) const override { mState.dump(out); }
 
         const compositionengine::Output& mOutput;
-        sp<compositionengine::LayerFE> mLayerFE;
+        compositionengine::LayerFE& mLayerFE;
         impl::OutputLayerCompositionState mState;
     };
 
     OutputLayerTest() {
-        EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
-        EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
+        ON_CALL(mLayerFE, getDebugName()).WillByDefault(Return("Test LayerFE"));
+        ON_CALL(mOutput, getName()).WillByDefault(ReturnRef(kOutputName));
 
-        EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
-        EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+        ON_CALL(mLayerFE, getCompositionState()).WillByDefault(Return(&mLayerFEState));
+        ON_CALL(mOutput, getState()).WillByDefault(ReturnRef(mOutputState));
     }
 
-    compositionengine::mock::Output mOutput;
-    sp<StrictMock<compositionengine::mock::LayerFE>> mLayerFE =
-            sp<StrictMock<compositionengine::mock::LayerFE>>::make();
+    NiceMock<compositionengine::mock::Output> mOutput;
+    sp<NiceMock<compositionengine::mock::LayerFE>> mLayerFE_ =
+            sp<NiceMock<compositionengine::mock::LayerFE>>::make();
+    NiceMock<compositionengine::mock::LayerFE>& mLayerFE = *mLayerFE_;
     OutputLayer mOutputLayer{mOutput, mLayerFE};
 
     LayerFECompositionState mLayerFEState;
@@ -530,7 +533,7 @@
 
 struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
     OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
-                                                    sp<compositionengine::LayerFE> layerFE)
+                                                    compositionengine::LayerFE& layerFE)
           : mOutput(output), mLayerFE(layerFE) {}
     // Mock everything called by updateCompositionState to simplify testing it.
     MOCK_CONST_METHOD1(calculateOutputSourceCrop, FloatRect(uint32_t));
@@ -539,7 +542,7 @@
 
     // compositionengine::OutputLayer overrides
     const compositionengine::Output& getOutput() const override { return mOutput; }
-    compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+    compositionengine::LayerFE& getLayerFE() const override { return mLayerFE; }
     const impl::OutputLayerCompositionState& getState() const override { return mState; }
     impl::OutputLayerCompositionState& editState() override { return mState; }
 
@@ -547,7 +550,7 @@
     MOCK_CONST_METHOD1(dumpState, void(std::string&));
 
     const compositionengine::Output& mOutput;
-    sp<compositionengine::LayerFE> mLayerFE;
+    compositionengine::LayerFE& mLayerFE;
     impl::OutputLayerCompositionState mState;
 };
 
@@ -588,7 +591,7 @@
 };
 
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
-    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+    EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
 
     mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
 }
@@ -774,7 +777,7 @@
     static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
-    static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
+    static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot;
     static constexpr bool kLayerGenericMetadata1Mandatory = true;
     static constexpr bool kLayerGenericMetadata2Mandatory = true;
     static constexpr float kWhitePointNits = 200.f;
@@ -823,7 +826,6 @@
         mLayerFEState.hdrMetadata = kHdrMetadata;
         mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
         mLayerFEState.buffer = kBuffer;
-        mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
         mLayerFEState.acquireFence = kFence;
 
         mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
@@ -834,7 +836,6 @@
         EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
                 .WillRepeatedly(Return(kSupportedPerFrameMetadata));
     }
-
     // Some tests may need to simulate unsupported HWC calls
     enum class SimulateUnsupported { None, ColorTransform };
 
@@ -953,13 +954,16 @@
 const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
 native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
         reinterpret_cast<native_handle_t*>(1031);
-const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer =
+        sp<GraphicBuffer>::make(1, 2, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer =
-        new GraphicBuffer(4, 5, PIXEL_FORMAT_RGBA_8888,
-                          AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
-                                  AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+        sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
-const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
+const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = sp<Fence>::make();
 const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
         "com.example.metadata.1";
 const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -969,7 +973,7 @@
         {4, 5, 6, 7}};
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
-    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+    EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -994,7 +998,7 @@
     expectPerFrameCommonCalls();
 
     expectNoSetCompositionTypeCall();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
+    EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1019,7 +1023,6 @@
     mLayerFEState.compositionType = Composition::SOLID_COLOR;
 
     expectPerFrameCommonCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     // Setting the composition type should happen before setting the color. We
     // check this in this test only by setting up an testing::InSeqeuence
@@ -1039,8 +1042,6 @@
     expectSetSidebandHandleCall();
     expectSetCompositionTypeCall(Composition::SIDEBAND);
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1052,8 +1053,6 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::CURSOR);
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1065,8 +1064,6 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1080,8 +1077,6 @@
     expectSetColorCall();
     expectNoSetCompositionTypeCall();
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1120,8 +1115,6 @@
     expectGenericLayerMetadataCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1134,8 +1127,6 @@
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
 
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
-
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
@@ -1150,7 +1141,6 @@
                               kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1166,7 +1156,6 @@
                               kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1182,7 +1171,6 @@
                               kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1198,7 +1186,6 @@
                               kOverrideSurfaceDamage, kOverrideLayerBrightness);
     expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1213,7 +1200,6 @@
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1230,7 +1216,6 @@
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::DEVICE);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1248,22 +1233,20 @@
                               Region::INVALID_REGION);
     expectSetHdrMetadataAndBufferCalls();
     expectSetCompositionTypeCall(Composition::CLIENT);
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) {
-    auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make();
-    OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE};
+    auto peekThroughLayerFE = sp<NiceMock<compositionengine::mock::LayerFE>>::make();
+    OutputLayer peekThroughLayer{mOutput, *peekThroughLayerFE};
 
     mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer;
 
     expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform,
                               Hwc2::IComposerClient::BlendMode::PREMULTIPLIED);
     expectPerFrameCommonCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1281,7 +1264,6 @@
 TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) {
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
                                  /*zIsOverridden*/ true, /*isPeekingThrough*/
@@ -1292,7 +1274,7 @@
 TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) {
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
+    EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
     expectSetCompositionTypeCall(Composition::CLIENT);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1304,7 +1286,7 @@
     expectGeometryCommonCalls();
     expectPerFrameCommonCalls();
     expectSetHdrMetadataAndBufferCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
     expectSetCompositionTypeCall(Composition::DEVICE);
 
     mLayerFEState.compositionType = Composition::DEVICE;
@@ -1323,7 +1305,6 @@
     expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
                               kSurfaceDamage, kLayerBrightness, blockingRegion);
     expectSetHdrMetadataAndBufferCalls();
-    EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
     expectSetCompositionTypeCall(Composition::DISPLAY_DECORATION);
 
     mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1331,6 +1312,105 @@
                                  false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) {
+    mLayerFEState.compositionType = Composition::REFRESH_RATE_INDICATOR;
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR);
+
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+/*
+ * OutputLayer::uncacheBuffers
+ */
+struct OutputLayerUncacheBufferTest : public OutputLayerTest {
+    static const sp<GraphicBuffer> kBuffer1;
+    static const sp<GraphicBuffer> kBuffer2;
+    static const sp<GraphicBuffer> kBuffer3;
+    static const sp<Fence> kFence;
+
+    OutputLayerUncacheBufferTest() {
+        auto& outputLayerState = mOutputLayer.editState();
+        outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer_);
+
+        mLayerFEState.compositionType = Composition::DEVICE;
+        mLayerFEState.acquireFence = kFence;
+
+        ON_CALL(mOutput, getDisplayColorProfile()).WillByDefault(Return(&mDisplayColorProfile));
+    }
+
+    std::shared_ptr<HWC2::mock::Layer> mHwcLayer_{std::make_shared<NiceMock<HWC2::mock::Layer>>()};
+    HWC2::mock::Layer& mHwcLayer = *mHwcLayer_;
+    NiceMock<mock::DisplayColorProfile> mDisplayColorProfile;
+};
+
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer1 =
+        sp<GraphicBuffer>::make(1, 2, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer2 =
+        sp<GraphicBuffer>::make(2, 3, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<GraphicBuffer> OutputLayerUncacheBufferTest::kBuffer3 =
+        sp<GraphicBuffer>::make(4, 5, PIXEL_FORMAT_RGBA_8888,
+                                AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                                        AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+const sp<Fence> OutputLayerUncacheBufferTest::kFence = sp<Fence>::make();
+
+TEST_F(OutputLayerUncacheBufferTest, canUncacheAndReuseSlot) {
+    // Buffer1 is stored in slot 0
+    mLayerFEState.buffer = kBuffer1;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 0, kBuffer1, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer2 is stored in slot 1
+    mLayerFEState.buffer = kBuffer2;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer2, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer3 is stored in slot 2
+    mLayerFEState.buffer = kBuffer3;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 2, kBuffer3, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer2 becomes the active buffer again (with a nullptr) and reuses slot 1
+    mLayerFEState.buffer = kBuffer2;
+    sp<GraphicBuffer> nullBuffer = nullptr;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, nullBuffer, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer slots are cleared
+    std::vector<uint32_t> slotsToClear = {0, 2, 1}; // order doesn't matter
+    EXPECT_CALL(mHwcLayer, setBufferSlotsToClear(slotsToClear, /*activeBufferSlot*/ 1));
+    // Uncache the active buffer in between other buffers to exercise correct algorithmic behavior.
+    mOutputLayer.uncacheBuffers({kBuffer1->getId(), kBuffer2->getId(), kBuffer3->getId()});
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+
+    // Buffer1 becomes active again, and rather than allocating a new slot, or re-using slot 0,
+    // the active buffer slot (slot 1 for Buffer2) is reused first, which allows HWC to free the
+    // memory for the active buffer. Note: slot 1 is different from the first and last buffer slot
+    // requested to be cleared in slotsToClear (slot 1), above, indicating that the algorithm
+    // correctly identifies the active buffer as the buffer in slot 1, despite ping-ponging.
+    mLayerFEState.buffer = kBuffer1;
+    EXPECT_CALL(mHwcLayer, setBuffer(/*slot*/ 1, kBuffer1, kFence));
+    mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
+                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+    Mock::VerifyAndClearExpectations(&mHwcLayer);
+}
+
 /*
  * OutputLayer::writeCursorPositionToHWC()
  */
@@ -1359,7 +1439,7 @@
 const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
 
 TEST_F(OutputLayerWriteCursorPositionToHWCTest, doesNothingIfNoFECompositionState) {
-    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+    EXPECT_CALL(mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
 
     mOutputLayer.writeCursorPositionToHWC();
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cf12890..9e0e7b5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -39,7 +39,6 @@
 #include "CallOrderStateMachineHelper.h"
 #include "MockHWC2.h"
 #include "RegionMatcher.h"
-#include "TestUtils.h"
 
 namespace android::compositionengine {
 namespace {
@@ -293,7 +292,7 @@
     InjectedLayer layer;
     layer.outputLayerState.overrideInfo.buffer = std::make_shared<
             renderengine::impl::
-                    ExternalTexture>(new GraphicBuffer(), renderEngine,
+                    ExternalTexture>(sp<GraphicBuffer>::make(), renderEngine,
                                      renderengine::impl::ExternalTexture::Usage::READABLE |
                                              renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     injectOutputLayer(layer);
@@ -759,56 +758,6 @@
 }
 
 /*
- * Output::updateLayerStateFromFE()
- */
-
-using OutputUpdateLayerStateFromFETest = OutputTest;
-
-TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
-    CompositionRefreshArgs refreshArgs;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-    EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = false;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
-    InjectedLayer layer1;
-    InjectedLayer layer2;
-    InjectedLayer layer3;
-
-    EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-    EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
-
-    injectOutputLayer(layer1);
-    injectOutputLayer(layer2);
-    injectOutputLayer(layer3);
-
-    CompositionRefreshArgs refreshArgs;
-    refreshArgs.updatingGeometryThisFrame = true;
-
-    mOutput->updateLayerStateFromFE(refreshArgs);
-}
-
-/*
  * Output::updateAndWriteCompositionState()
  */
 
@@ -850,20 +799,17 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -890,20 +836,17 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -929,20 +872,17 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -968,8 +908,7 @@
     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(*layer1.outputLayer, requiresClientComposition())
-                .WillRepeatedly(Return(false));
+    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));
 
@@ -977,9 +916,7 @@
     EXPECT_CALL(*layer0.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer0.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
-
+    EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     // After calling planComposition (which clears overrideInfo), this test sets
     // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
@@ -989,18 +926,15 @@
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/
                                 true));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     injectOutputLayer(layer0);
     injectOutputLayer(layer1);
@@ -1017,7 +951,7 @@
 
     std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
             renderengine::impl::
-                    ExternalTexture>(new GraphicBuffer(), renderEngine,
+                    ExternalTexture>(sp<GraphicBuffer>::make(), renderEngine,
                                      renderengine::impl::ExternalTexture::Usage::READABLE |
                                              renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     layer1.outputLayerState.overrideInfo.buffer = buffer;
@@ -1101,10 +1035,10 @@
         MOCK_METHOD1(
                 chooseCompositionStrategyAsync,
                 std::future<bool>(std::optional<android::HWComposer::DeviceRequestedChanges>*));
-        MOCK_METHOD4(composeSurfaces,
-                     std::optional<base::unique_fd>(
-                             const Region&, const compositionengine::CompositionRefreshArgs&,
-                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+        MOCK_METHOD3(composeSurfaces,
+                     std::optional<base::unique_fd>(const Region&,
+                                                    std::shared_ptr<renderengine::ExternalTexture>,
+                                                    base::unique_fd&));
         MOCK_METHOD0(resetCompositionStrategy, void());
     };
 
@@ -1138,9 +1072,9 @@
     EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_))
             .WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
                             Return(ByMove(p.get_future()))));
-    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+    EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
 
-    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
     EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::SUCCESS);
     EXPECT_FALSE(result.bufferAvailable());
 }
@@ -1163,7 +1097,7 @@
             .WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
                             Return(ByMove(p.get_future()))));
 
-    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
     EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_FALSE(result.bufferAvailable());
 }
@@ -1191,9 +1125,9 @@
     EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
         return p.get_future();
     });
-    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+    EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
 
-    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
     EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_TRUE(result.bufferAvailable());
 }
@@ -1223,9 +1157,9 @@
     EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
         return p.get_future();
     });
-    EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+    EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
 
-    impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+    impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
     EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
     EXPECT_TRUE(result.bufferAvailable());
 }
@@ -1243,14 +1177,49 @@
                           compositionengine::LayerFESet&));
     };
 
+    OutputPrepareTest() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+
+        mRefreshArgs.layers.push_back(mLayer1.layerFE);
+        mRefreshArgs.layers.push_back(mLayer2.layerFE);
+    }
+
+    struct Layer {
+        StrictMock<mock::OutputLayer> outputLayer;
+        sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
+    };
+
     StrictMock<OutputPartialMock> mOutput;
     CompositionRefreshArgs mRefreshArgs;
     LayerFESet mGeomSnapshots;
+    Layer mLayer1;
+    Layer mLayer2;
 };
 
-TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) {
     InSequence seq;
+
+    mRefreshArgs.bufferIdsToUncache = {1, 3, 5};
+
     EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+    EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+    EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+
+    mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) {
+    InSequence seq;
+
+    mRefreshArgs.bufferIdsToUncache = {};
+
+    EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+    EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0);
+    EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0);
 
     mOutput.prepare(mRefreshArgs, mGeomSnapshots);
 }
@@ -1537,9 +1506,6 @@
 
 TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
     EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
-    EXPECT_CALL(*mLayer.layerFE,
-                prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
-
     mGeomSnapshots.clear();
 
     ensureOutputLayerIfVisible();
@@ -2054,11 +2020,9 @@
         MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD0(beginFrame, void());
         MOCK_METHOD0(prepareFrame, void());
-        MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+        MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
         MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
-        MOCK_METHOD2(finishFrame,
-                     void(const compositionengine::CompositionRefreshArgs&,
-                          GpuCompositionResult&&));
+        MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
@@ -2080,7 +2044,7 @@
     EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
     EXPECT_CALL(mOutput, prepareFrame());
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
-    EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+    EXPECT_CALL(mOutput, finishFrame(_));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
@@ -2098,9 +2062,9 @@
     EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
     EXPECT_CALL(mOutput, beginFrame());
     EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
-    EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+    EXPECT_CALL(mOutput, prepareFrameAsync());
     EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
-    EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+    EXPECT_CALL(mOutput, finishFrame(_));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
 
@@ -2135,6 +2099,7 @@
         mOutput.setDisplayColorProfileForTest(
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+        mOutput.editState().isEnabled = true;
 
         EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
                 .WillRepeatedly(Return(&mLayer1.mOutputLayer));
@@ -2998,10 +2963,10 @@
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
         MOCK_METHOD(Region, getDirtyRegion, (), (const));
-        MOCK_METHOD4(composeSurfaces,
-                     std::optional<base::unique_fd>(
-                             const Region&, const compositionengine::CompositionRefreshArgs&,
-                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+        MOCK_METHOD3(composeSurfaces,
+                     std::optional<base::unique_fd>(const Region&,
+                                                    std::shared_ptr<renderengine::ExternalTexture>,
+                                                    base::unique_fd&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD0(prepareFrame, void());
         MOCK_METHOD0(updateProtectedContentState, void());
@@ -3065,7 +3030,7 @@
     EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
     EXPECT_CALL(mOutput, updateProtectedContentState());
     EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
     EXPECT_CALL(mOutput, postFramebuffer());
     EXPECT_CALL(mOutput, prepareFrame());
@@ -3081,10 +3046,10 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
         // mock implementations.
-        MOCK_METHOD4(composeSurfaces,
-                     std::optional<base::unique_fd>(
-                             const Region&, const compositionengine::CompositionRefreshArgs&,
-                             std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+        MOCK_METHOD3(composeSurfaces,
+                     std::optional<base::unique_fd>(const Region&,
+                                                    std::shared_ptr<renderengine::ExternalTexture>,
+                                                    base::unique_fd&));
         MOCK_METHOD0(postFramebuffer, void());
         MOCK_METHOD0(updateProtectedContentState, void());
         MOCK_METHOD2(dequeueRenderBuffer,
@@ -3100,24 +3065,23 @@
     StrictMock<OutputPartialMock> mOutput;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
-    CompositionRefreshArgs mRefreshArgs;
 };
 
 TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
     mOutput.mState.isEnabled = false;
 
     impl::GpuCompositionResult result;
-    mOutput.finishFrame(mRefreshArgs, std::move(result));
+    mOutput.finishFrame(std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
     mOutput.mState.isEnabled = true;
     EXPECT_CALL(mOutput, updateProtectedContentState());
     EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _));
 
     impl::GpuCompositionResult result;
-    mOutput.finishFrame(mRefreshArgs, std::move(result));
+    mOutput.finishFrame(std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
@@ -3126,12 +3090,12 @@
     InSequence seq;
     EXPECT_CALL(mOutput, updateProtectedContentState());
     EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
-    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
             .WillOnce(Return(ByMove(base::unique_fd())));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
 
     impl::GpuCompositionResult result;
-    mOutput.finishFrame(mRefreshArgs, std::move(result));
+    mOutput.finishFrame(std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, predictionSucceeded) {
@@ -3141,7 +3105,7 @@
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
 
     impl::GpuCompositionResult result;
-    mOutput.finishFrame(mRefreshArgs, std::move(result));
+    mOutput.finishFrame(std::move(result));
 }
 
 TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
@@ -3157,11 +3121,11 @@
                                                                       2);
 
     EXPECT_CALL(mOutput,
-                composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+                composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer,
                                 Eq(ByRef(result.fence))))
             .WillOnce(Return(ByMove(base::unique_fd())));
     EXPECT_CALL(*mRenderSurface, queueBuffer(_));
-    mOutput.finishFrame(mRefreshArgs, std::move(result));
+    mOutput.finishFrame(std::move(result));
 }
 
 /*
@@ -3227,7 +3191,6 @@
     // setup below are satisfied in the specific order.
     InSequence seq;
 
-    EXPECT_CALL(*mRenderSurface, flip());
     EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
@@ -3250,7 +3213,6 @@
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
     frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
 
-    EXPECT_CALL(*mRenderSurface, flip());
     EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
@@ -3258,16 +3220,19 @@
     // 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) {
+    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) {
+    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) {
+    EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_, _))
+            .WillOnce([&layer3Fence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                     ui::LayerStack) {
                 EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
             });
 
@@ -3284,7 +3249,6 @@
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
     frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
 
-    EXPECT_CALL(*mRenderSurface, flip());
     EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
     EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
 
@@ -3320,21 +3284,23 @@
     Output::FrameFences frameFences;
     frameFences.presentFence = presentFence;
 
-    EXPECT_CALL(*mRenderSurface, flip());
     EXPECT_CALL(mOutput, presentAndGetFrameFences()).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) {
+    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) {
+    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) {
+    EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_, _))
+            .WillOnce([&presentFence](ftl::SharedFuture<FenceResult> futureFenceResult,
+                                      ui::LayerStack) {
                 EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
             });
 
@@ -3356,7 +3322,8 @@
         // mock implementations.
         MOCK_CONST_METHOD0(getSkipColorTransform, bool());
         MOCK_METHOD3(generateClientCompositionRequests,
-                     std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&));
+                     std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace,
+                                                         std::vector<LayerFE*>&));
         MOCK_METHOD2(appendRegionFlashRequests,
                      void(const Region&, std::vector<LayerFE::LayerSettings>&));
         MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3389,8 +3356,7 @@
 
         EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
-        EXPECT_CALL(mCompositionEngine, getTimeStats())
-                .WillRepeatedly(ReturnRef(*mTimeStats.get()));
+        EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get()));
         EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
                 .WillRepeatedly(ReturnRef(kHdrCapabilities));
     }
@@ -3403,8 +3369,8 @@
                     getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
             if (success) {
                 getInstance()->mReadyFence =
-                        getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
-                                                               externalTexture, fence);
+                        getInstance()->mOutput.composeSurfaces(kDebugRegion, externalTexture,
+                                                               fence);
             }
             return nextState<FenceCheckState>();
         }
@@ -3449,7 +3415,7 @@
     StrictMock<OutputPartialMock> mOutput;
     std::shared_ptr<renderengine::ExternalTexture> mOutputBuffer = std::make_shared<
             renderengine::impl::
-                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                    ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
                                      renderengine::impl::ExternalTexture::Usage::READABLE |
                                              renderengine::impl::ExternalTexture::Usage::WRITEABLE);
 
@@ -3531,9 +3497,8 @@
             .WillRepeatedly([&](const renderengine::DisplaySettings&,
                                 const std::vector<renderengine::LayerSettings>&,
                                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&)
-                                    -> std::future<renderengine::RenderEngineResult> {
-                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
     verify().execute().expectAFenceWasReturned();
 }
@@ -3563,9 +3528,8 @@
             .WillRepeatedly([&](const renderengine::DisplaySettings&,
                                 const std::vector<renderengine::LayerSettings>&,
                                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&)
-                                    -> std::future<renderengine::RenderEngineResult> {
-                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3598,9 +3562,8 @@
             .WillRepeatedly([&](const renderengine::DisplaySettings&,
                                 const std::vector<renderengine::LayerSettings>&,
                                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&)
-                                    -> std::future<renderengine::RenderEngineResult> {
-                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3626,10 +3589,8 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
             .Times(2)
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))))
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3657,8 +3618,7 @@
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
     verify().execute().expectAFenceWasReturned();
@@ -3687,7 +3647,7 @@
 
     const auto otherOutputBuffer = std::make_shared<
             renderengine::impl::
-                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                    ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
                                      renderengine::impl::ExternalTexture::Usage::READABLE |
                                              renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
@@ -3697,9 +3657,8 @@
             .WillRepeatedly([&](const renderengine::DisplaySettings&,
                                 const std::vector<renderengine::LayerSettings>&,
                                 const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                                base::unique_fd&&)
-                                    -> std::future<renderengine::RenderEngineResult> {
-                return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
+                return ftl::yield<FenceResult>(Fence::NO_FENCE);
             });
 
     verify().execute().expectAFenceWasReturned();
@@ -3730,11 +3689,9 @@
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     verify().execute().expectAFenceWasReturned();
     EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3811,8 +3768,7 @@
           : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
         auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
             EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
-                    .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()}))));
+                    .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
             return nextState<ExecuteState>();
         }
     };
@@ -4065,60 +4021,30 @@
                 .WillRepeatedly(Return());
         EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
         EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-                .WillRepeatedly(
-                        [&](const renderengine::DisplaySettings&,
-                            const std::vector<renderengine::LayerSettings>&,
-                            const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                            base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
-                            return futureOf<renderengine::RenderEngineResult>(
-                                    {NO_ERROR, base::unique_fd()});
-                        });
+                .WillRepeatedly([&](const renderengine::DisplaySettings&,
+                                    const std::vector<renderengine::LayerSettings>&,
+                                    const std::shared_ptr<renderengine::ExternalTexture>&,
+                                    const bool, base::unique_fd&&) -> ftl::Future<FenceResult> {
+                    return ftl::yield<FenceResult>(Fence::NO_FENCE);
+                });
     }
 
     Layer mLayer1;
     Layer mLayer2;
 };
 
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
-    mOutput.mState.isSecure = false;
-    mLayer2.mLayerFEState.hasProtectedContent = true;
-    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(false));
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
-    mOutput.mState.isSecure = true;
-    mLayer2.mLayerFEState.hasProtectedContent = true;
-    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
     mOutput.mState.isSecure = true;
     mLayer2.mLayerFEState.hasProtectedContent = false;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(false));
     EXPECT_CALL(*mRenderSurface, setProtected(false));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
     mOutput.updateProtectedContentState();
     mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+    mOutput.composeSurfaces(kDebugRegion, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -4129,81 +4055,44 @@
     // For this test, we also check the call order of key functions.
     InSequence seq;
 
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, setProtected(true));
     // Must happen after setting the protected content state.
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
     mOutput.updateProtectedContentState();
     mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+    mOutput.composeSurfaces(kDebugRegion, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
     mOutput.mState.isSecure = true;
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
     mOutput.updateProtectedContentState();
     mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
-    mOutput.mState.isSecure = true;
-    mLayer2.mLayerFEState.hasProtectedContent = true;
-    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
-    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
-    mOutput.mState.isSecure = true;
-    mLayer2.mLayerFEState.hasProtectedContent = true;
-    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
-    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
-    EXPECT_CALL(*mRenderSurface, setProtected(true));
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+    mOutput.composeSurfaces(kDebugRegion, tex, fd);
 }
 
 TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
     mOutput.mState.isSecure = true;
     mLayer2.mLayerFEState.hasProtectedContent = true;
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
-    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
     EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
-    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
     mOutput.updateProtectedContentState();
     mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+    mOutput.composeSurfaces(kDebugRegion, tex, fd);
 }
 
 struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -4230,69 +4119,13 @@
 
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     base::unique_fd fd;
     std::shared_ptr<renderengine::ExternalTexture> tex;
     mOutput.updateProtectedContentState();
     mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
-      : public OutputComposeSurfacesTest_SetsExpensiveRendering {
-    OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
-        mLayer.layerFEState.backgroundBlurRadius = 10;
-        mLayer.layerFEState.isOpaque = false;
-        mOutput.editState().isEnabled = true;
-
-        EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
-        EXPECT_CALL(mLayer.outputLayer,
-                    writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
-                                    /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
-                .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
-        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
-                .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
-                        {NO_ERROR, base::unique_fd()}))));
-        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
-        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
-                .WillRepeatedly(Return(&mLayer.outputLayer));
-    }
-
-    NonInjectedLayer mLayer;
-    compositionengine::CompositionRefreshArgs mRefreshArgs;
-};
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
-    mRefreshArgs.blursAreExpensive = true;
-    mOutput.updateCompositionState(mRefreshArgs);
-    mOutput.planComposition();
-    mOutput.writeCompositionState(mRefreshArgs);
-
-    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
-    mRefreshArgs.blursAreExpensive = false;
-    mOutput.updateCompositionState(mRefreshArgs);
-    mOutput.planComposition();
-    mOutput.writeCompositionState(mRefreshArgs);
-
-    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
-
-    base::unique_fd fd;
-    std::shared_ptr<renderengine::ExternalTexture> tex;
-    mOutput.updateProtectedContentState();
-    mOutput.dequeueRenderBuffer(&fd, &tex);
-    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
+    mOutput.composeSurfaces(kDebugRegion, tex, fd);
 }
 
 /*
@@ -4303,7 +4136,7 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // compositionengine::Output overrides
         std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper(
-            bool supportsProtectedContent, ui::Dataspace dataspace) {
+                bool supportsProtectedContent, ui::Dataspace dataspace) {
             std::vector<LayerFE*> ignore;
             return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
                                                                    dataspace, ignore);
@@ -4312,6 +4145,8 @@
 
     struct Layer {
         Layer() {
+            EXPECT_CALL(mOutputLayer, getOverrideCompositionSettings())
+                    .WillRepeatedly(Return(std::nullopt));
             EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
             EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
             EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*mLayerFE));
@@ -4393,8 +4228,9 @@
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
 }
 
@@ -4403,29 +4239,26 @@
     mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
     mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     EXPECT_EQ(0u, requests.size());
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings)));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
-
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
-    ASSERT_EQ(3u, requests.size());
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
-    EXPECT_EQ(mShadowSettings, requests[1]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
@@ -4442,27 +4275,22 @@
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) {
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
-
     mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true;
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[1].mLayerSettings)));
     EXPECT_CALL(*mLayers[2].mLayerFE,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
-
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
-    ASSERT_EQ(3u, requests.size());
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
-    EXPECT_EQ(mShadowSettings, requests[1]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 
     // Check that a timestamp was set for the layers that generated requests
     EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
@@ -4484,11 +4312,12 @@
     mLayers[1].mLayerFEState.isOpaque = true;
     mLayers[2].mLayerFEState.isOpaque = true;
 
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
 }
@@ -4507,11 +4336,12 @@
     mLayers[1].mLayerFEState.isOpaque = false;
     mLayers[2].mLayerFEState.isOpaque = false;
 
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
     EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
 }
@@ -4546,6 +4376,7 @@
             true /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4558,6 +4389,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
     LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -4566,13 +4398,14 @@
     mBlackoutSettings.alpha = 0.f;
     mBlackoutSettings.disableBlending = true;
 
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mBlackoutSettings)));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     ASSERT_EQ(2u, requests.size());
 
     // The second layer is expected to be rendered as alpha=0 black with no blending
@@ -4598,6 +4431,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(Rect(0, 0, 30, 30)),
@@ -4610,6 +4444,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(Rect(0, 0, 40, 201)),
@@ -4622,18 +4457,19 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                      kDisplayDataspace));
+                                                            kDisplayDataspace));
 }
 
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4652,6 +4488,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4664,6 +4501,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4676,14 +4514,15 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4706,6 +4545,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4718,6 +4558,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4730,14 +4571,15 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4759,6 +4601,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4771,6 +4614,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4783,14 +4627,15 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
     static_cast<void>(
             mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
@@ -4810,6 +4655,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4822,6 +4668,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4834,17 +4681,19 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
-    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
 
-    static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
-                                                                kDisplayDataspace));
+    static_cast<void>(
+            mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
+                                                            kDisplayDataspace));
 }
 
 TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) {
@@ -4857,14 +4706,12 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
     layer2.layerFEState.isOpaque = true;
@@ -4893,20 +4740,17 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
     layer2.layerFEState.isOpaque = false;
@@ -4936,20 +4780,17 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
-    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     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));
-    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
-            .WillRepeatedly(Return(false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
 
     BlurRegion region;
     layer2.layerFEState.blurRegions.push_back(region);
@@ -5018,12 +4859,13 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
     EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
+    EXPECT_CALL(*leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(leftLayer.mLayerSettings)));
 
     compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
             Region(Rect(1000, 0, 2000, 1000)),
@@ -5036,16 +4878,17 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
     EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
     EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
+    EXPECT_CALL(*rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(rightLayer.mLayerSettings)));
 
     constexpr bool supportsProtectedContent = true;
-    auto requests =
-        mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace);
+    auto requests = mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent,
+                                                                    kOutputDataspace);
     ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
     EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -5069,6 +4912,7 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
     LayerFE::LayerSettings mShadowSettings;
@@ -5079,11 +4923,12 @@
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mShadowSettings)));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
 
     EXPECT_EQ(mShadowSettings, requests[0]);
@@ -5097,9 +4942,6 @@
     const Region kPartialContentWithPartialShadowRegion =
             Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
 
-    LayerFE::LayerSettings mShadowSettings;
-    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
-
     mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
     mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
 
@@ -5114,20 +4956,20 @@
             false /* clearContent */,
             compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
             kLayerWhitePointNits,
+            false /* treat170mAsSrgb */,
     };
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
     EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
-    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
-            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
-                    {mShadowSettings, mLayers[2].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
 
-    auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
-                                                              kDisplayDataspace);
-    ASSERT_EQ(2u, requests.size());
+    auto requests =
+            mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+                                                            kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
 
-    EXPECT_EQ(mShadowSettings, requests[0]);
-    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index e5f9ebf..83937a6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -61,8 +61,8 @@
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<mock::Display> mDisplay;
-    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
-    sp<mock::DisplaySurface> mDisplaySurface = new StrictMock<mock::DisplaySurface>();
+    sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make();
+    sp<mock::DisplaySurface> mDisplaySurface = sp<StrictMock<mock::DisplaySurface>>::make();
     impl::RenderSurface mSurface{mCompositionEngine, mDisplay,
                                  RenderSurfaceCreationArgsBuilder()
                                          .setDisplayWidth(DEFAULT_DISPLAY_WIDTH)
@@ -109,7 +109,7 @@
  */
 
 TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) {
-    sp<Fence> fence = new Fence();
+    sp<Fence> fence = sp<Fence>::make();
 
     EXPECT_CALL(*mDisplaySurface, getClientTargetAcquireFence()).WillOnce(ReturnRef(fence));
 
@@ -234,7 +234,7 @@
  */
 
 TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
 
     EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
             .WillOnce(
@@ -253,7 +253,7 @@
 TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) {
     const auto buffer = std::make_shared<
             renderengine::impl::
-                    ExternalTexture>(new GraphicBuffer(), mRenderEngine,
+                    ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
                                      renderengine::impl::ExternalTexture::Usage::READABLE |
                                              renderengine::impl::ExternalTexture::Usage::WRITEABLE);
     mSurface.mutableTextureForTest() = buffer;
@@ -271,8 +271,9 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) {
-    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
-                                                                              mRenderEngine, false);
+    const auto buffer =
+            std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(),
+                                                                  mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -290,8 +291,9 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) {
-    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
-                                                                              mRenderEngine, false);
+    const auto buffer =
+            std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(),
+                                                                  mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -309,7 +311,7 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
 
     impl::OutputCompositionState state;
     state.usesClientComposition = false;
@@ -329,8 +331,9 @@
 }
 
 TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirtualDisplay) {
-    const auto buffer = std::make_shared<renderengine::impl::ExternalTexture>(new GraphicBuffer(),
-                                                                              mRenderEngine, false);
+    const auto buffer =
+            std::make_shared<renderengine::impl::ExternalTexture>(sp<GraphicBuffer>::make(),
+                                                                  mRenderEngine, false);
     mSurface.mutableTextureForTest() = buffer;
 
     impl::OutputCompositionState state;
@@ -359,17 +362,5 @@
     mSurface.onPresentDisplayCompleted();
 }
 
-/*
- * RenderSurface::flip()
- */
-
-TEST_F(RenderSurfaceTest, flipForwardsSignal) {
-    mSurface.setPageFlipCountForTest(500);
-
-    mSurface.flip();
-
-    EXPECT_EQ(501u, mSurface.getPageFlipCount());
-}
-
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 0e9db36..bd030d0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -28,8 +28,6 @@
 #include <utils/Errors.h>
 #include <memory>
 
-#include "tests/TestUtils.h"
-
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 
@@ -345,19 +343,18 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings& displaySettings,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -365,15 +362,13 @@
         EXPECT_EQ(0.5f, layers[0].alpha);
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
-            .WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
-            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false)))
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = false;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
@@ -397,19 +392,18 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings& displaySettings,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -418,15 +412,13 @@
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
-            .WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
-            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true)))
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
@@ -450,31 +442,30 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
 
     mOutputState.displayBrightnessNits = 400.f;
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings& displaySettings,
-                const std::vector<renderengine::LayerSettings>&,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList1));
+            .WillOnce(Return(clientComp1));
     EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList2));
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
@@ -501,31 +492,30 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
 
     mOutputState.displayBrightnessNits = 400.f;
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings& displaySettings,
-                const std::vector<renderengine::LayerSettings>&,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>&,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList1));
+            .WillOnce(Return(clientComp1));
     EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+                prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
                         mOutputState.displayBrightnessNits)))
-            .WillOnce(Return(clientCompList2));
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     mOutputState.isSecure = true;
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false);
@@ -549,21 +539,20 @@
     CachedSet cachedSet(layer1);
     cachedSet.append(CachedSet(layer2));
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    clientCompList1[0].alpha = 0.5f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    clientComp1->alpha = 0.5f;
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    clientCompList2[0].alpha = 0.75f;
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    clientComp2->alpha = 0.75f;
 
     mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings& displaySettings,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
         EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
         EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
@@ -572,11 +561,11 @@
         EXPECT_EQ(0.75f, layers[1].alpha);
         EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
     cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
     expectReadyBuffer(cachedSet);
@@ -588,6 +577,20 @@
     cachedSet.append(CachedSet(layer3));
 }
 
+TEST_F(CachedSetTest, cachingHintIncludesLayersByDefault) {
+    CachedSet cachedSet(*mTestLayers[0]->cachedSetLayer.get());
+    EXPECT_FALSE(cachedSet.cachingHintExcludesLayers());
+}
+
+TEST_F(CachedSetTest, cachingHintExcludesLayersWhenDisabled) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    mTestLayers[0]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+    mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+    CachedSet cachedSet(layer1);
+    EXPECT_TRUE(cachedSet.cachingHintExcludesLayers());
+}
+
 TEST_F(CachedSetTest, holePunch_requiresBuffer) {
     CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
     auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
@@ -659,6 +662,26 @@
     EXPECT_FALSE(cachedSet.requiresHolePunch());
 }
 
+TEST_F(CachedSetTest, holePunch_requiresNonHdrWithExtendedBrightness) {
+    const auto dataspace = static_cast<ui::Dataspace>(ui::Dataspace::STANDARD_DCI_P3 |
+                                                      ui::Dataspace::TRANSFER_SRGB |
+                                                      ui::Dataspace::RANGE_EXTENDED);
+    mTestLayers[0]->outputLayerCompositionState.dataspace = dataspace;
+    mTestLayers[0]->layerFECompositionState.currentHdrSdrRatio = 5.f;
+    mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer);
+
+    CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
+    auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
+    layerFECompositionState.blendMode = hal::BlendMode::NONE;
+    sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE;
+
+    CachedSet cachedSet(layer);
+    EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+    EXPECT_FALSE(cachedSet.requiresHolePunch());
+}
+
 TEST_F(CachedSetTest, holePunch_requiresNoBlending) {
     CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get();
     auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState;
@@ -773,28 +796,27 @@
 
     cachedSet.addHolePunchLayerIfFeasible(layer3, true);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    clientCompList3[0].source.buffer.buffer =
+    clientComp3->source.buffer.buffer =
             std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
                                                                       1ULL /* bufferId */,
                                                                       HAL_PIXEL_FORMAT_RGBA_8888,
                                                                       0ULL /*usage*/);
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
+    EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3));
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings&,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
@@ -814,7 +836,7 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
@@ -822,7 +844,7 @@
 }
 
 TEST_F(CachedSetTest, addHolePunch_noBuffer) {
-    // Same as addHolePunch, except that clientCompList3 does not contain a
+    // Same as addHolePunch, except that clientComp3 does not contain a
     // buffer. This imitates the case where the buffer had protected content, so
     // BufferLayer did not add it to the LayerSettings. This should not assert.
     mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
@@ -840,22 +862,21 @@
 
     cachedSet.addHolePunchLayerIfFeasible(layer3, true);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
-    EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
-    EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
+    EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
+    EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
+    EXPECT_CALL(*layerFE3, prepareClientComposition(_)).WillOnce(Return(clientComp3));
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings&,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 4u);
@@ -876,7 +897,7 @@
             EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
         }
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
@@ -974,40 +995,39 @@
 
     cachedSet.addBackgroundBlurLayer(layer3);
 
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
-    clientCompList1.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
-    clientCompList2.push_back({});
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
-    clientCompList3.push_back({});
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp1;
+    clientComp1.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp2;
+    clientComp2.emplace();
+    std::optional<compositionengine::LayerFE::LayerSettings> clientComp3;
+    clientComp3.emplace();
 
-    clientCompList3[0].source.buffer.buffer =
+    clientComp3->source.buffer.buffer =
             std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/,
                                                                       1ULL /* bufferId */,
                                                                       HAL_PIXEL_FORMAT_RGBA_8888,
                                                                       0ULL /*usage*/);
 
     EXPECT_CALL(*layerFE1,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 Enabled)))
-            .WillOnce(Return(clientCompList1));
+            .WillOnce(Return(clientComp1));
     EXPECT_CALL(*layerFE2,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 Enabled)))
-            .WillOnce(Return(clientCompList2));
+            .WillOnce(Return(clientComp2));
     EXPECT_CALL(*layerFE3,
-                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
                         compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
                                 BackgroundBlurOnly)))
-            .WillOnce(Return(clientCompList3));
+            .WillOnce(Return(clientComp3));
 
-    const auto drawLayers =
-            [&](const renderengine::DisplaySettings&,
-                const std::vector<renderengine::LayerSettings>& layers,
-                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<renderengine::LayerSettings>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&) -> ftl::Future<FenceResult> {
         // If the highlight layer is enabled, it will increase the size by 1.
         // We're interested in the third layer either way.
         EXPECT_GE(layers.size(), 3u);
@@ -1016,7 +1036,7 @@
         EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor);
         EXPECT_EQ(0.0f, blurSettings.alpha);
 
-        return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+        return ftl::yield<FenceResult>(Fence::NO_FENCE);
     };
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 96021ec..778a0a8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -27,8 +27,6 @@
 #include <renderengine/mock/RenderEngine.h>
 #include <chrono>
 
-#include "tests/TestUtils.h"
-
 namespace android::compositionengine {
 using namespace std::chrono_literals;
 using impl::planner::CachedSet;
@@ -108,11 +106,12 @@
         testLayer->outputLayerCompositionState.visibleRegion =
                 Region(Rect(pos + 1, pos + 1, pos + 2, pos + 2));
 
+        const auto kUsageFlags =
+                static_cast<uint64_t>(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
+                                      GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE);
         testLayer->layerFECompositionState.buffer =
-                new GraphicBuffer(100, 100, HAL_PIXEL_FORMAT_RGBA_8888, 1,
-                                  GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
-                                          GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
-                                  "output");
+                sp<GraphicBuffer>::make(100u, 100u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags,
+                                        "output");
 
         testLayer->layerFE = sp<mock::LayerFE>::make();
 
@@ -123,12 +122,11 @@
         EXPECT_CALL(*testLayer->layerFE, getCompositionState)
                 .WillRepeatedly(Return(&testLayer->layerFECompositionState));
 
-        std::vector<LayerFE::LayerSettings> clientCompositionList = {
-                LayerFE::LayerSettings{},
-        };
+        std::optional<LayerFE::LayerSettings> clientComposition;
+        clientComposition.emplace();
 
-        EXPECT_CALL(*testLayer->layerFE, prepareClientCompositionList)
-                .WillRepeatedly(Return(clientCompositionList));
+        EXPECT_CALL(*testLayer->layerFE, prepareClientComposition)
+                .WillRepeatedly(Return(clientComposition));
         EXPECT_CALL(testLayer->outputLayer, getLayerFE)
                 .WillRepeatedly(ReturnRef(*testLayer->layerFE));
         EXPECT_CALL(testLayer->outputLayer, getState)
@@ -171,8 +169,7 @@
 void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -423,8 +420,7 @@
     layerState1->resetFramesSinceBufferUpdate();
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -447,8 +443,7 @@
     mTime += 200ms;
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -500,8 +495,7 @@
     layerState3->resetFramesSinceBufferUpdate();
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -515,8 +509,7 @@
 
     // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -545,8 +538,7 @@
     layerState3->incrementFramesSinceBufferUpdate();
     mTime += 200ms;
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     initializeOverrideBuffer(layers);
     EXPECT_NE(getNonBufferHash(layers),
               mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -601,8 +593,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -637,16 +628,15 @@
 
     EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState1.get(),
@@ -667,8 +657,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -711,16 +700,15 @@
 
     EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState0.get(),
@@ -741,8 +729,7 @@
     // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
     // exception that there would be a hole punch above it.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -783,16 +770,15 @@
 
     EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
 
-    std::vector<LayerFE::LayerSettings> clientCompositionList = {
-            LayerFE::LayerSettings{},
-    };
-    clientCompositionList[0].source.buffer.buffer = std::make_shared<
+    std::optional<LayerFE::LayerSettings> clientComposition;
+    clientComposition.emplace();
+    clientComposition->source.buffer.buffer = std::make_shared<
             renderengine::impl::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
                                                  mRenderEngine,
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          READABLE);
-    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
-            .WillOnce(Return(clientCompositionList));
+    EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientComposition(_))
+            .WillOnce(Return(clientComposition));
 
     const std::vector<const LayerState*> layers = {
             layerState0.get(),
@@ -813,8 +799,7 @@
     // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
     // exception that there would be a hole punch above it.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -865,8 +850,7 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -911,8 +895,7 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillRepeatedly(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillRepeatedly(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -965,8 +948,7 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -1014,8 +996,7 @@
 
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -1057,8 +1038,7 @@
     mTime += 200ms;
     // layers would be flattened but the buffer would not be overridden
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
 
     initializeOverrideBuffer(layers);
     EXPECT_EQ(getNonBufferHash(layers),
@@ -1125,14 +1105,62 @@
     }
 
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState,
                                  std::chrono::steady_clock::now() -
                                          (kCachedSetRenderDuration + 10ms),
                                  true);
 }
 
+TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+    auto& layerState2 = mTestLayers[1]->layerState;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+    // The third layer has a CachingHint that prevents caching from running
+    auto& layerState3 = mTestLayers[2]->layerState;
+    const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+    mTestLayers[2]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled;
+    mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerState3.get(),
+    };
+
+    initializeFlattener(layers);
+
+    mTime += 200ms;
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+    // This will render a CachedSet.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+    // We've rendered a CachedSet, but we haven't merged it in.
+    EXPECT_EQ(nullptr, overrideBuffer1);
+    EXPECT_EQ(nullptr, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+
+    // This time we merge the CachedSet in, so we have a new hash, and we should
+    // only have two sets.
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+    EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
 TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) {
     auto& layerState1 = mTestLayers[0]->layerState;
     const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
@@ -1162,8 +1190,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1213,8 +1240,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1264,8 +1290,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1318,8 +1343,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
@@ -1371,8 +1395,7 @@
 
     // This will render a CachedSet.
     EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
-            .WillOnce(Return(ByMove(
-                    futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+            .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
     mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
 
     // We've rendered a CachedSet, but we haven't merged it in.
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 5c6e8da..044917e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -112,6 +112,9 @@
     sp<mock::LayerFE> mLayerFE = sp<mock::LayerFE>::make();
     mock::OutputLayer mOutputLayer;
     std::unique_ptr<LayerState> mLayerState;
+
+    static constexpr auto kUsageFlags = static_cast<uint32_t>(
+            AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN);
 };
 
 TEST_F(LayerStateTest, getOutputLayer) {
@@ -346,7 +349,7 @@
 TEST_F(LayerStateTest, updateBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -354,7 +357,7 @@
     mock::OutputLayer newOutputLayer;
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
@@ -364,7 +367,7 @@
 TEST_F(LayerStateTest, updateBufferSingleBufferedLegacy) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -372,7 +375,7 @@
     mock::OutputLayer newOutputLayer;
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
 
@@ -388,7 +391,7 @@
 TEST_F(LayerStateTest, updateBufferSingleBufferedUsage) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -396,7 +399,7 @@
     mock::OutputLayer newOutputLayer;
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     layerFECompositionStateTwo.buffer->usage = static_cast<uint64_t>(BufferUsage::FRONT_BUFFER);
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
@@ -412,14 +415,14 @@
 TEST_F(LayerStateTest, compareBuffer) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
     mock::OutputLayer newOutputLayer;
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
@@ -720,10 +723,7 @@
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
     layerFECompositionState.buffer =
-            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
-                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
-                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
-                              "buffer1");
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, kUsageFlags, "buffer1");
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -732,10 +732,7 @@
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
     layerFECompositionStateTwo.buffer =
-            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
-                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
-                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
-                              "buffer2");
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBX_8888, kUsageFlags, "buffer2");
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
@@ -748,10 +745,7 @@
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
     layerFECompositionState.buffer =
-            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_8888,
-                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
-                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
-                              "buffer1");
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, kUsageFlags, "buffer1");
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -759,10 +753,7 @@
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
     layerFECompositionStateTwo.buffer =
-            new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888,
-                              AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
-                                      AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
-                              "buffer2");
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBX_8888, kUsageFlags, "buffer2");
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
@@ -1003,6 +994,45 @@
     EXPECT_TRUE(mLayerState->hasBlurBehind());
 }
 
+TEST_F(LayerStateTest, updateCachingHint) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+    EXPECT_EQ(ftl::Flags<LayerStateField>(LayerStateField::CachingHint), updates);
+}
+
+TEST_F(LayerStateTest, compareCachingHint) {
+    OutputLayerCompositionState outputLayerCompositionState;
+    LayerFECompositionState layerFECompositionState;
+    layerFECompositionState.cachingHint = gui::CachingHint::Enabled;
+    setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+                       layerFECompositionState);
+    mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+    mock::OutputLayer newOutputLayer;
+    sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+    LayerFECompositionState layerFECompositionStateTwo;
+    layerFECompositionStateTwo.cachingHint = gui::CachingHint::Disabled;
+    setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+                       layerFECompositionStateTwo);
+    auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+    verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::CachingHint);
+
+    EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+    EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
 TEST_F(LayerStateTest, dumpDoesNotCrash) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
@@ -1069,7 +1099,7 @@
 TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) {
     OutputLayerCompositionState outputLayerCompositionState;
     LayerFECompositionState layerFECompositionState;
-    layerFECompositionState.buffer = new GraphicBuffer();
+    layerFECompositionState.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
                        layerFECompositionState);
     mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -1077,7 +1107,7 @@
     mock::OutputLayer newOutputLayer;
     sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
                        layerFECompositionStateTwo);
     auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 68c72e0..35d0ffb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -228,7 +228,7 @@
 }
 
 TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) {
-    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make();
     mock::OutputLayer outputLayerOne;
     sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
     OutputLayerCompositionState outputLayerCompositionStateOne;
@@ -268,7 +268,7 @@
             .dataspace = ui::Dataspace::SRGB,
     };
     LayerFECompositionState layerFECompositionStateOne;
-    layerFECompositionStateOne.buffer = new GraphicBuffer();
+    layerFECompositionStateOne.buffer = sp<GraphicBuffer>::make();
     layerFECompositionStateOne.alpha = sAlphaOne;
     layerFECompositionStateOne.colorTransformIsIdentity = true;
     setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne,
@@ -285,7 +285,7 @@
             .dataspace = ui::Dataspace::DISPLAY_P3,
     };
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     layerFECompositionStateTwo.alpha = sAlphaTwo;
     layerFECompositionStateTwo.colorTransformIsIdentity = false;
     layerFECompositionStateTwo.colorTransform = sMat4One;
@@ -310,7 +310,7 @@
             .sourceCrop = sFloatRectOne,
     };
     LayerFECompositionState layerFECompositionStateOne;
-    layerFECompositionStateOne.buffer = new GraphicBuffer();
+    layerFECompositionStateOne.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne,
                        layerFECompositionStateOne);
     LayerState layerStateOne(&outputLayerOne);
@@ -321,7 +321,7 @@
             .sourceCrop = sFloatRectTwo,
     };
     LayerFECompositionState layerFECompositionStateTwo;
-    layerFECompositionStateTwo.buffer = new GraphicBuffer();
+    layerFECompositionStateTwo.buffer = sp<GraphicBuffer>::make();
     setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo,
                        layerFECompositionStateTwo);
     LayerState layerStateTwo(&outputLayerTwo);
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
deleted file mode 100644
index 3ccc229..0000000
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "ContainerLayer"
-
-#include "ContainerLayer.h"
-
-namespace android {
-
-ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {}
-
-ContainerLayer::~ContainerLayer() = default;
-
-bool ContainerLayer::isVisible() const {
-    return false;
-}
-
-sp<Layer> ContainerLayer::createClone() {
-    sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
-            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
-    layer->setInitialValuesForClone(this);
-    return layer;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
deleted file mode 100644
index 9b7bab1..0000000
--- a/services/surfaceflinger/ContainerLayer.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 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 <sys/types.h>
-
-#include <cstdint>
-
-#include "Layer.h"
-
-namespace android {
-
-class ContainerLayer : public Layer {
-public:
-    explicit ContainerLayer(const LayerCreationArgs&);
-    ~ContainerLayer() override;
-
-    const char* getType() const override { return "ContainerLayer"; }
-    bool isVisible() const override;
-
-    bool isCreatedFromMainThread() const override { return true; }
-
-protected:
-    bool canDrawShadows() const override { return false; }
-    sp<Layer> createClone() override;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Display/DisplayMap.h b/services/surfaceflinger/Display/DisplayMap.h
new file mode 100644
index 0000000..0d59706
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayMap.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android::display {
+
+// The static capacities were chosen to exceed a typical number of physical and/or virtual displays.
+
+template <typename Key, typename Value>
+using DisplayMap = ftl::SmallMap<Key, Value, 5>;
+
+template <typename Key, typename Value>
+using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>;
+
+template <typename T>
+using PhysicalDisplayVector = ftl::SmallVector<T, 3>;
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h
new file mode 100644
index 0000000..d07cdf5
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeRequest.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <ftl/non_null.h>
+
+#include <scheduler/FrameRateMode.h>
+
+namespace android::display {
+
+struct DisplayModeRequest {
+    scheduler::FrameRateMode mode;
+
+    // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
+    bool emitEvent = false;
+};
+
+inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) {
+    return lhs.mode == rhs.mode && lhs.emitEvent == rhs.emitEvent;
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.cpp b/services/surfaceflinger/Display/DisplaySnapshot.cpp
new file mode 100644
index 0000000..0c7a58e
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <algorithm>
+#include <functional>
+#include <utility>
+
+#include <ftl/algorithm.h>
+#include <ftl/enum.h>
+#include <ui/DebugUtils.h>
+
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+DisplaySnapshot::DisplaySnapshot(PhysicalDisplayId displayId,
+                                 ui::DisplayConnectionType connectionType,
+                                 DisplayModes&& displayModes, ui::ColorModes&& colorModes,
+                                 std::optional<DeviceProductInfo>&& deviceProductInfo)
+      : mDisplayId(displayId),
+        mConnectionType(connectionType),
+        mDisplayModes(std::move(displayModes)),
+        mColorModes(std::move(colorModes)),
+        mDeviceProductInfo(std::move(deviceProductInfo)) {}
+
+std::optional<DisplayModeId> DisplaySnapshot::translateModeId(hal::HWConfigId hwcId) const {
+    return ftl::find_if(mDisplayModes,
+                        [hwcId](const DisplayModes::value_type& pair) {
+                            return pair.second->getHwcId() == hwcId;
+                        })
+            .transform(&ftl::to_key<DisplayModes>);
+}
+
+ui::ColorModes DisplaySnapshot::filterColorModes(bool supportsWideColor) const {
+    ui::ColorModes modes = mColorModes;
+
+    // If the display is internal and the configuration claims it's not wide color capable, filter
+    // out all wide color modes. The typical reason why this happens is that the hardware is not
+    // good enough to support GPU composition of wide color, and thus the OEMs choose to disable
+    // this capability.
+    if (mConnectionType == ui::DisplayConnectionType::Internal && !supportsWideColor) {
+        const auto it = std::remove_if(modes.begin(), modes.end(), ui::isWideColorMode);
+        modes.erase(it, modes.end());
+    }
+
+    return modes;
+}
+
+void DisplaySnapshot::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
+
+    dumper.dump("connectionType"sv, ftl::enum_string(mConnectionType));
+
+    dumper.dump("colorModes"sv);
+    {
+        utils::Dumper::Indent indent(dumper);
+        for (const auto mode : mColorModes) {
+            dumper.dump({}, decodeColorMode(mode));
+        }
+    }
+
+    dumper.dump("deviceProductInfo"sv, mDeviceProductInfo);
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplaySnapshot.h b/services/surfaceflinger/Display/DisplaySnapshot.h
new file mode 100644
index 0000000..23471f5
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplaySnapshot.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <optional>
+
+#include <ui/ColorMode.h>
+#include <ui/DisplayId.h>
+#include <ui/StaticDisplayInfo.h>
+
+#include "DisplayHardware/DisplayMode.h"
+#include "Utils/Dumper.h"
+
+namespace android::display {
+
+// Immutable state of a physical display, captured on hotplug.
+class DisplaySnapshot {
+public:
+    DisplaySnapshot(PhysicalDisplayId, ui::DisplayConnectionType, DisplayModes&&, ui::ColorModes&&,
+                    std::optional<DeviceProductInfo>&&);
+
+    DisplaySnapshot(const DisplaySnapshot&) = delete;
+    DisplaySnapshot(DisplaySnapshot&&) = default;
+
+    PhysicalDisplayId displayId() const { return mDisplayId; }
+    ui::DisplayConnectionType connectionType() const { return mConnectionType; }
+
+    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
+
+    const auto& displayModes() const { return mDisplayModes; }
+    const auto& colorModes() const { return mColorModes; }
+    const auto& deviceProductInfo() const { return mDeviceProductInfo; }
+
+    ui::ColorModes filterColorModes(bool supportsWideColor) const;
+
+    void dump(utils::Dumper&) const;
+
+private:
+    const PhysicalDisplayId mDisplayId;
+    const ui::DisplayConnectionType mConnectionType;
+
+    // Effectively const except in move constructor.
+    DisplayModes mDisplayModes;
+    ui::ColorModes mColorModes;
+    std::optional<DeviceProductInfo> mDeviceProductInfo;
+};
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
new file mode 100644
index 0000000..cba1014
--- /dev/null
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -0,0 +1,76 @@
+/*
+ * 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 <functional>
+#include <utility>
+
+#include <binder/IBinder.h>
+#include <ui/DisplayId.h>
+#include <utils/StrongPointer.h>
+
+#include "DisplayMap.h"
+#include "DisplaySnapshot.h"
+
+namespace android::display {
+
+// TODO(b/229877597): Replace with AIDL type.
+using DisplayToken = IBinder;
+
+class PhysicalDisplay {
+public:
+    template <typename... Args>
+    PhysicalDisplay(sp<DisplayToken> token, Args&&... args)
+          : mToken(std::move(token)), mSnapshot(std::forward<Args>(args)...) {}
+
+    PhysicalDisplay(const PhysicalDisplay&) = delete;
+    PhysicalDisplay(PhysicalDisplay&&) = default;
+
+    const sp<DisplayToken>& token() const { return mToken; }
+    const DisplaySnapshot& snapshot() const { return mSnapshot; }
+
+    // Transformers for PhysicalDisplays::get.
+
+    using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
+    SnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
+
+    bool isInternal() const {
+        return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal;
+    }
+
+    // Predicate for ftl::find_if on PhysicalDisplays.
+    static constexpr auto hasToken(const sp<DisplayToken>& token) {
+        return [&token](const std::pair<const PhysicalDisplayId, PhysicalDisplay>& pair) {
+            return pair.second.token() == token;
+        };
+    }
+
+private:
+    const sp<DisplayToken> mToken;
+
+    // Effectively const except in move constructor.
+    DisplaySnapshot mSnapshot;
+};
+
+using PhysicalDisplays = PhysicalDisplayMap<PhysicalDisplayId, PhysicalDisplay>;
+
+// Combinator for ftl::Optional<PhysicalDisplayId>::and_then.
+constexpr auto getPhysicalDisplay(const PhysicalDisplays& displays) {
+    return [&](PhysicalDisplayId id) { return displays.get(id); };
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index b49c95d..f6ca9e2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -39,7 +39,9 @@
 #include <system/window.h>
 #include <ui/GraphicTypes.h>
 
+#include "Display/DisplaySnapshot.h"
 #include "DisplayDevice.h"
+#include "FrontEnd/DisplayInfo.h"
 #include "Layer.h"
 #include "RefreshRateOverlay.h"
 #include "SurfaceFlinger.h"
@@ -48,8 +50,6 @@
 
 namespace hal = hardware::graphics::composer::hal;
 
-ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
-
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
         const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken,
         std::shared_ptr<compositionengine::Display> compositionDisplay)
@@ -63,14 +63,14 @@
         mHwComposer(args.hwComposer),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mConnectionType(args.connectionType),
         mCompositionDisplay{args.compositionDisplay},
         mActiveModeFPSTrace("ActiveModeFPS -" + to_string(getId())),
         mActiveModeFPSHwcTrace("ActiveModeFPS_HWC -" + to_string(getId())),
+        mRenderFrameRateFPSTrace("RenderRateFPS -" + to_string(getId())),
         mPhysicalOrientation(args.physicalOrientation),
-        mSupportedModes(std::move(args.supportedModes)),
         mIsPrimary(args.isPrimary),
-        mRefreshRateConfigs(std::move(args.refreshRateConfigs)) {
+        mRequestedRefreshRate(args.requestedRefreshRate),
+        mRefreshRateSelector(std::move(args.refreshRateSelector)) {
     mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
             compositionengine::RenderSurfaceCreationArgsBuilder()
@@ -104,7 +104,9 @@
 
     mCompositionDisplay->getRenderSurface()->initialize();
 
-    if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value());
+    if (const auto powerModeOpt = args.initialPowerMode) {
+        setPowerMode(*powerModeOpt);
+    }
 
     // initialize the display orientation transform.
     setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
@@ -132,15 +134,7 @@
     }
 }
 
-void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
-    mDeviceProductInfo = std::move(info);
-}
-
-uint32_t DisplayDevice::getPageFlipCount() const {
-    return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
-}
-
-auto DisplayDevice::getInputInfo() const -> InputInfo {
+auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
     gui::DisplayInfo info;
     info.displayId = getLayerStack().id;
 
@@ -169,7 +163,11 @@
     return {.info = info,
             .transform = displayTransform,
             .receivesInput = receivesInput(),
-            .isSecure = isSecure()};
+            .isSecure = isSecure(),
+            .isPrimary = isPrimary(),
+            .isVirtual = isVirtual(),
+            .rotationFlags = ui::Transform::toRotationFlags(mOrientation),
+            .transformHint = getTransformHint()};
 }
 
 void DisplayDevice::setPowerMode(hal::PowerMode mode) {
@@ -182,10 +180,21 @@
         getCompositionDisplay()->applyDisplayBrightness(true);
     }
 
-    mPowerMode = mode;
+    if (mPowerMode) {
+        *mPowerMode = mode;
+    } else {
+        mPowerMode.emplace("PowerMode -" + to_string(getId()), mode);
+    }
 
-    getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() &&
-                                                   *mPowerMode != hal::PowerMode::OFF);
+    getCompositionDisplay()->setCompositionEnabled(isPoweredOn());
+}
+
+void DisplayDevice::tracePowerMode() {
+    // assign the same value for tracing
+    if (mPowerMode) {
+        const hal::PowerMode powerMode = *mPowerMode;
+        *mPowerMode = powerMode;
+    }
 }
 
 void DisplayDevice::enableLayerCaching(bool enable) {
@@ -200,56 +209,32 @@
     return mPowerMode && *mPowerMode != hal::PowerMode::OFF;
 }
 
-void DisplayDevice::setActiveMode(DisplayModeId id) {
-    const auto mode = getMode(id);
-    LOG_FATAL_IF(!mode, "Cannot set active mode which is not supported.");
-    ATRACE_INT(mActiveModeFPSTrace.c_str(), mode->getFps().getIntValue());
-    mActiveMode = mode;
-    if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->setActiveModeId(mActiveMode->getId());
-    }
+void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps renderFps) {
+    ATRACE_INT(mActiveModeFPSTrace.c_str(), displayFps.getIntValue());
+    ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue());
+
+    mRefreshRateSelector->setActiveMode(modeId, renderFps);
+
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(mActiveMode->getFps());
+        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
     }
 }
 
 status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info,
                                            const hal::VsyncPeriodChangeConstraints& constraints,
                                            hal::VsyncPeriodChangeTimeline* outTimeline) {
-    if (!info.mode || info.mode->getPhysicalDisplayId() != getPhysicalId()) {
+    if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) {
         ALOGE("Trying to initiate a mode change to invalid mode %s on display %s",
-              info.mode ? std::to_string(info.mode->getId().value()).c_str() : "null",
+              info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str()
+                           : "null",
               to_string(getId()).c_str());
         return BAD_VALUE;
     }
-    mNumModeSwitchesInPolicy++;
     mUpcomingActiveMode = info;
-    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue());
-    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(),
-                                                    constraints, outTimeline);
-}
-
-const DisplayModePtr& DisplayDevice::getActiveMode() const {
-    return mActiveMode;
-}
-
-const DisplayModes& DisplayDevice::getSupportedModes() const {
-    return mSupportedModes;
-}
-
-DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const {
-    const DisplayModePtr nullMode;
-    return mSupportedModes.get(modeId).value_or(std::cref(nullMode));
-}
-
-std::optional<DisplayModeId> DisplayDevice::translateModeId(hal::HWConfigId hwcId) const {
-    const auto it =
-            std::find_if(mSupportedModes.begin(), mSupportedModes.end(),
-                         [hwcId](const auto& pair) { return pair.second->getHwcId() == hwcId; });
-    if (it != mSupportedModes.end()) {
-        return it->second->getId();
-    }
-    return {};
+    ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.modeOpt->modePtr->getFps().getIntValue());
+    return mHwComposer.setActiveModeWithConstraints(getPhysicalId(),
+                                                    info.modeOpt->modePtr->getHwcId(), constraints,
+                                                    outTimeline);
 }
 
 nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
@@ -264,27 +249,17 @@
         return vsyncPeriod;
     }
 
-    return getActiveMode()->getFps().getPeriodNsecs();
-}
-
-nsecs_t DisplayDevice::getRefreshTimestamp() const {
-    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    const auto vsyncPeriodNanos = getVsyncPeriodFromHWC();
-    return now - ((now - mLastHwVsync) % vsyncPeriodNanos);
-}
-
-void DisplayDevice::onVsync(nsecs_t timestamp) {
-    mLastHwVsync = timestamp;
+    return refreshRateSelector().getActiveMode().modePtr->getVsyncPeriod();
 }
 
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
 
-void DisplayDevice::setLayerStack(ui::LayerStack stack) {
-    mCompositionDisplay->setLayerFilter({stack, isInternal()});
+void DisplayDevice::setLayerFilter(ui::LayerFilter filter) {
+    mCompositionDisplay->setLayerFilter(filter);
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->setLayerStack(stack);
+        mRefreshRateOverlay->setLayerStack(filter.layerStack);
     }
 }
 
@@ -305,10 +280,6 @@
                                   Rect orientedDisplaySpaceRect) {
     mOrientation = orientation;
 
-    if (isPrimary()) {
-        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
-    }
-
     // We need to take care of display rotation for globalTransform for case if the panel is not
     // installed aligned with device orientation.
     const auto transformOrientation = orientation + mPhysicalOrientation;
@@ -349,48 +320,14 @@
     return mStagedBrightness;
 }
 
-ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
-    return sPrimaryDisplayRotationFlags;
-}
+void DisplayDevice::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
 
-std::string DisplayDevice::getDebugName() const {
-    using namespace std::string_literals;
+    dumper.dump("name"sv, '"' + mDisplayName + '"');
+    dumper.dump("powerMode"sv, mPowerMode);
 
-    std::string name = "Display "s + to_string(getId()) + " ("s;
-
-    if (mConnectionType) {
-        name += isInternal() ? "internal"s : "external"s;
-    } else {
-        name += "virtual"s;
-    }
-
-    if (isPrimary()) {
-        name += ", primary"s;
-    }
-
-    return name + ", \""s + mDisplayName + "\")"s;
-}
-
-void DisplayDevice::dump(std::string& result) const {
-    using namespace std::string_literals;
-
-    result += getDebugName();
-
-    if (!isVirtual()) {
-        result += "\n   deviceProductInfo="s;
-        if (mDeviceProductInfo) {
-            mDeviceProductInfo->dump(result);
-        } else {
-            result += "{}"s;
-        }
-    }
-
-    result += "\n   powerMode="s;
-    result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)";
-    result += '\n';
-
-    if (mRefreshRateConfigs) {
-        mRefreshRateConfigs->dump(result);
+    if (mRefreshRateSelector) {
+        mRefreshRateSelector->dump(dumper);
     }
 }
 
@@ -414,10 +351,6 @@
     return mCompositionDisplay->getState().undefinedRegion;
 }
 
-bool DisplayDevice::needsFiltering() const {
-    return mCompositionDisplay->getState().needsFiltering;
-}
-
 ui::LayerStack DisplayDevice::getLayerStack() const {
     return mCompositionDisplay->getState().layerFilter.layerStack;
 }
@@ -478,26 +411,51 @@
                            capabilities.getDesiredMinLuminance());
 }
 
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
+                                             bool showRenderRate, bool showInMiddle) {
     if (!enable) {
         mRefreshRateOverlay.reset();
         return;
     }
 
-    const auto fpsRange = mRefreshRateConfigs->getSupportedRefreshRateRange();
-    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+    ftl::Flags<RefreshRateOverlay::Features> features;
+    if (showSpinner) {
+        features |= RefreshRateOverlay::Features::Spinner;
+    }
+
+    if (showRenderRate) {
+        features |= RefreshRateOverlay::Features::RenderRate;
+    }
+
+    if (showInMiddle) {
+        features |= RefreshRateOverlay::Features::ShowInMiddle;
+    }
+
+    if (setByHwc) {
+        features |= RefreshRateOverlay::Features::SetByHwc;
+    }
+
+    const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
+    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
     mRefreshRateOverlay->setLayerStack(getLayerStack());
     mRefreshRateOverlay->setViewport(getSize());
-    mRefreshRateOverlay->changeRefreshRate(getActiveMode()->getFps());
+    updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
+}
+
+void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) {
+    ATRACE_CALL();
+    if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) {
+        mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
+    }
 }
 
 bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
                                          bool timerExpired) {
-    if (mRefreshRateConfigs && mRefreshRateOverlay) {
-        const auto newRefreshRate =
-                mRefreshRateConfigs->onKernelTimerChanged(desiredModeId, timerExpired);
-        if (newRefreshRate) {
-            mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+    if (mRefreshRateSelector && mRefreshRateOverlay) {
+        const auto newMode =
+                mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
+        if (newMode) {
+            updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps);
             return true;
         }
     }
@@ -511,13 +469,15 @@
     }
 }
 
-bool DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
+        -> DesiredActiveModeAction {
     ATRACE_CALL();
 
-    LOG_ALWAYS_FATAL_IF(!info.mode, "desired mode not provided");
-    LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.mode->getPhysicalDisplayId(), "DisplayId mismatch");
+    LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
+    LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(),
+                        "DisplayId mismatch");
 
-    ALOGV("%s(%s)", __func__, to_string(*info.mode).c_str());
+    ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str());
 
     std::scoped_lock lock(mActiveModeLock);
     if (mDesiredActiveModeChanged) {
@@ -525,18 +485,31 @@
         const auto prevConfig = mDesiredActiveMode.event;
         mDesiredActiveMode = info;
         mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
-        return false;
+        return DesiredActiveModeAction::None;
     }
 
+    const auto& desiredMode = *info.modeOpt->modePtr;
+
     // Check if we are already at the desired mode
-    if (!force && getActiveMode()->getId() == info.mode->getId()) {
-        return false;
+    const auto currentMode = refreshRateSelector().getActiveMode();
+    if (!force && currentMode.modePtr->getId() == desiredMode.getId()) {
+        if (currentMode == info.modeOpt) {
+            return DesiredActiveModeAction::None;
+        }
+
+        setActiveMode(desiredMode.getId(), desiredMode.getFps(), info.modeOpt->fps);
+        return DesiredActiveModeAction::InitiateRenderRateSwitch;
     }
 
+    // Set the render frame rate to the current physical refresh rate to schedule the next
+    // frame as soon as possible.
+    setActiveMode(currentMode.modePtr->getId(), currentMode.modePtr->getFps(),
+                  currentMode.modePtr->getFps());
+
     // Initiate a mode change.
     mDesiredActiveModeChanged = true;
     mDesiredActiveMode = info;
-    return true;
+    return DesiredActiveModeAction::InitiateDisplayModeSwitch;
 }
 
 std::optional<DisplayDevice::ActiveModeInfo> DisplayDevice::getDesiredActiveMode() const {
@@ -551,25 +524,26 @@
     mDesiredActiveModeChanged = false;
 }
 
-status_t DisplayDevice::setRefreshRatePolicy(
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
-    const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy();
-    const status_t setPolicyResult = overridePolicy
-            ? mRefreshRateConfigs->setOverridePolicy(policy)
-            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
-
-    if (setPolicyResult == OK) {
-        const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0);
-
-        ALOGI("Display %s policy changed\n"
-              "Previous: {%s}\n"
-              "Current:  {%s}\n"
-              "%d mode changes were performed under the previous policy",
-              to_string(getId()).c_str(), oldPolicy.toString().c_str(),
-              policy ? policy->toString().c_str() : "null", numModeChanges);
+void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
+    using fps_approx_ops::operator<=;
+    if (mRequestedRefreshRate <= 0_Hz) {
+        return;
     }
 
-    return setPolicyResult;
+    using fps_approx_ops::operator>;
+    if (mRequestedRefreshRate > pacesetterDisplayRefreshRate) {
+        mAdjustedRefreshRate = pacesetterDisplayRefreshRate;
+        return;
+    }
+
+    unsigned divisor = static_cast<unsigned>(
+            std::floor(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue()));
+    if (divisor == 0) {
+        mAdjustedRefreshRate = 0_Hz;
+        return;
+    }
+
+    mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor;
 }
 
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b91dece..dc5f8a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -24,6 +24,7 @@
 #include <android-base/thread_annotations.h>
 #include <android/native_window.h>
 #include <binder/IBinder.h>
+#include <ftl/concat.h>
 #include <gui/LayerState.h>
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
@@ -41,13 +42,15 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 
+#include "Display/DisplayModeRequest.h"
 #include "DisplayHardware/DisplayMode.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayHardware/PowerAdvisor.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "ThreadContext.h"
 #include "TracedOrdinal.h"
-
+#include "Utils/Dumper.h"
 namespace android {
 
 class Fence;
@@ -65,6 +68,10 @@
 class DisplaySurface;
 } // namespace compositionengine
 
+namespace display {
+class DisplaySnapshot;
+} // namespace display
+
 class DisplayDevice : public RefBase {
 public:
     constexpr static float sDefaultMinLumiance = 0.0;
@@ -80,11 +87,8 @@
         return mCompositionDisplay;
     }
 
-    std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; }
-
-    bool isVirtual() const { return !mConnectionType; }
+    bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); }
     bool isPrimary() const { return mIsPrimary; }
-    bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
@@ -94,7 +98,7 @@
     int getHeight() const;
     ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    void setLayerStack(ui::LayerStack);
+    void setLayerFilter(ui::LayerFilter);
     void setDisplaySize(int width, int height);
     void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
     void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
@@ -105,14 +109,11 @@
     ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
     ui::Rotation getOrientation() const { return mOrientation; }
 
-    static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
-
     std::optional<float> getStagedBrightness() const REQUIRES(kMainThreadContext);
     ui::Transform::RotationFlags getTransformHint() const;
     const ui::Transform& getTransform() const;
     const Rect& getLayerStackSpaceRect() const;
     const Rect& getOrientedDisplaySpaceRect() const;
-    bool needsFiltering() const;
     ui::LayerStack getLayerStack() const;
     bool receivesInput() const { return mFlags & eReceivesInput; }
 
@@ -164,19 +165,7 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
-    void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
-    const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
-        return mDeviceProductInfo;
-    }
-
-    struct InputInfo {
-        gui::DisplayInfo info;
-        ui::Transform transform;
-        bool receivesInput;
-        bool isSecure;
-    };
-
-    InputInfo getInputInfo() const;
+    surfaceflinger::frontend::DisplayInfo getFrontEndInfo() const;
 
     /* ------------------------------------------------------------------------
      * Display power mode management.
@@ -184,6 +173,7 @@
     std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
+    void tracePowerMode();
 
     // Enables layer caching on this DisplayDevice
     void enableLayerCaching(bool enable);
@@ -193,135 +183,136 @@
     /* ------------------------------------------------------------------------
      * Display mode management.
      */
-    const DisplayModePtr& getActiveMode() const;
 
+    // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest.
     struct ActiveModeInfo {
-        DisplayModePtr mode;
-        scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
+        using Event = scheduler::DisplayModeEvent;
+
+        ActiveModeInfo() = default;
+        ActiveModeInfo(scheduler::FrameRateMode mode, Event event)
+              : modeOpt(std::move(mode)), event(event) {}
+
+        explicit ActiveModeInfo(display::DisplayModeRequest&& request)
+              : ActiveModeInfo(std::move(request.mode),
+                               request.emitEvent ? Event::Changed : Event::None) {}
+
+        ftl::Optional<scheduler::FrameRateMode> modeOpt;
+        Event event = Event::None;
 
         bool operator!=(const ActiveModeInfo& other) const {
-            return mode != other.mode || event != other.event;
+            return modeOpt != other.modeOpt || event != other.event;
         }
     };
 
-    bool setDesiredActiveMode(const ActiveModeInfo&, bool force = false) EXCLUDES(mActiveModeLock);
+    enum class DesiredActiveModeAction {
+        None,
+        InitiateDisplayModeSwitch,
+        InitiateRenderRateSwitch
+    };
+    DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
+            EXCLUDES(mActiveModeLock);
     std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
     void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
     ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
         return mUpcomingActiveMode;
     }
 
-    void setActiveMode(DisplayModeId) REQUIRES(kMainThreadContext);
+    scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
+        return mRefreshRateSelector->getActiveMode();
+    }
+
+    void setActiveMode(DisplayModeId, Fps displayFps, Fps renderFps);
+
     status_t initiateModeChange(const ActiveModeInfo&,
                                 const hal::VsyncPeriodChangeConstraints& constraints,
                                 hal::VsyncPeriodChangeTimeline* outTimeline)
             REQUIRES(kMainThreadContext);
 
-    // Return the immutable list of supported display modes. The HWC may report different modes
-    // after a hotplug reconnect event, in which case the DisplayDevice object will be recreated.
-    // Hotplug reconnects are common for external displays.
-    const DisplayModes& getSupportedModes() const;
+    scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
 
-    // Returns nullptr if the given mode ID is not supported. A previously
-    // supported mode may be no longer supported for some devices like TVs and
-    // set-top boxes after a hotplug reconnect.
-    DisplayModePtr getMode(DisplayModeId) const;
-
-    std::optional<DisplayModeId> translateModeId(hal::HWConfigId) const;
-
-    // Returns the refresh rate configs for this display.
-    scheduler::RefreshRateConfigs& refreshRateConfigs() const { return *mRefreshRateConfigs; }
-
-    // Returns a shared pointer to the refresh rate configs for this display.
-    // Clients can store this refresh rate configs and use it even if the DisplayDevice
-    // is destroyed.
-    std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const {
-        return mRefreshRateConfigs;
+    // Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
+    std::shared_ptr<scheduler::RefreshRateSelector> holdRefreshRateSelector() const {
+        return mRefreshRateSelector;
     }
 
     // Enables an overlay to be displayed with the current refresh rate
-    void enableRefreshRateOverlay(bool enable, bool showSpinner);
+    void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
+                                  bool showInMiddle) REQUIRES(kMainThreadContext);
+    void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false);
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
     void animateRefreshRateOverlay();
 
-    void onVsync(nsecs_t timestamp);
     nsecs_t getVsyncPeriodFromHWC() const;
-    nsecs_t getRefreshTimestamp() const;
 
-    status_t setRefreshRatePolicy(
-            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy,
-            bool overridePolicy);
+    Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
+
+    // Round the requested refresh rate to match a divisor of the pacesetter
+    // display's refresh rate. Only supported for virtual displays.
+    void adjustRefreshRate(Fps pacesetterDisplayRefreshRate);
 
     // release HWC resources (if any) for removable displays
     void disconnect();
 
-    /* ------------------------------------------------------------------------
-     * Debugging
-     */
-    uint32_t getPageFlipCount() const;
-    std::string getDebugName() const;
-    void dump(std::string& result) const;
+    void dump(utils::Dumper&) const;
 
 private:
     const sp<SurfaceFlinger> mFlinger;
     HWComposer& mHwComposer;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
-    const std::optional<ui::DisplayConnectionType> mConnectionType;
 
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
     std::string mDisplayName;
     std::string mActiveModeFPSTrace;
     std::string mActiveModeFPSHwcTrace;
+    std::string mRenderFrameRateFPSTrace;
 
     const ui::Rotation mPhysicalOrientation;
     ui::Rotation mOrientation = ui::ROTATION_0;
 
-    static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
+    // Allow nullopt as initial power mode.
+    using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>;
+    std::optional<TracedPowerMode> mPowerMode;
 
-     // allow initial power mode as null.
-    std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode;
-    DisplayModePtr mActiveMode;
     std::optional<float> mStagedBrightness;
     std::optional<float> mBrightness;
-    const DisplayModes mSupportedModes;
-
-    std::atomic<nsecs_t> mLastHwVsync = 0;
 
     // TODO(b/182939859): Remove special cases for primary display.
     const bool mIsPrimary;
 
     uint32_t mFlags = 0;
 
-    std::optional<DeviceProductInfo> mDeviceProductInfo;
+    // Requested refresh rate in fps, supported only for virtual displays.
+    // when this value is non zero, SurfaceFlinger will try to drop frames
+    // for virtual displays to match this requested refresh rate.
+    const Fps mRequestedRefreshRate;
+
+    // Adjusted refresh rate, rounded to match a divisor of the pacesetter
+    // display's refresh rate. Only supported for virtual displays.
+    Fps mAdjustedRefreshRate = 0_Hz;
 
     std::vector<ui::Hdr> mOverrideHdrTypes;
 
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
     std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
 
     mutable std::mutex mActiveModeLock;
     ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock);
-    TracedOrdinal<bool> mDesiredActiveModeChanged
-            GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false};
+    TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) =
+            {ftl::Concat("DesiredActiveModeChanged-", getId().value).c_str(), false};
     ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext);
-
-    std::atomic_int mNumModeSwitchesInPolicy = 0;
 };
 
 struct DisplayDeviceState {
     struct Physical {
         PhysicalDisplayId id;
-        ui::DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-        std::optional<DeviceProductInfo> deviceProductInfo;
-        DisplayModes supportedModes;
         DisplayModePtr activeMode;
 
         bool operator==(const Physical& other) const {
-            return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
+            return id == other.id && hwcDisplayId == other.hwcDisplayId;
         }
     };
 
@@ -339,6 +330,8 @@
     uint32_t height = 0;
     std::string displayName;
     bool isSecure = false;
+    // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
+    Fps requestedRefreshRate;
 
 private:
     static std::atomic<int32_t> sNextSequenceId;
@@ -354,10 +347,9 @@
     HWComposer& hwComposer;
     const wp<IBinder> displayToken;
     const std::shared_ptr<compositionengine::Display> compositionDisplay;
-    std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> refreshRateSelector;
 
     int32_t sequenceId{0};
-    std::optional<ui::DisplayConnectionType> connectionType;
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<compositionengine::DisplaySurface> displaySurface;
@@ -368,8 +360,9 @@
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
     std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode;
     bool isPrimary{false};
-    DisplayModes supportedModes;
     DisplayModeId activeModeId;
+    // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
+    Fps requestedRefreshRate;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3651231..f7049b9 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -23,6 +23,7 @@
 #include <android-base/file.h>
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_manager.h>
+#include <gui/TraceUtils.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
@@ -55,6 +56,10 @@
 using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
 using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
 using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlHdrConversionCapability =
+        aidl::android::hardware::graphics::common::HdrConversionCapability;
+using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy;
+using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
 using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
 using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
 using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
@@ -203,6 +208,12 @@
         return ::ndk::ScopedAStatus::ok();
     }
 
+    ::ndk::ScopedAStatus onRefreshRateChangedDebug(
+            const RefreshRateChangedDebugData& refreshRateChangedDebugData) override {
+        mCallback.onRefreshRateChangedDebug(refreshRateChangedDebugData);
+        return ::ndk::ScopedAStatus::ok();
+    }
+
 private:
     HWC2::ComposerCallback& mCallback;
 };
@@ -229,6 +240,27 @@
         return;
     }
 
+    addReader(translate<Display>(kSingleReaderKey));
+
+    // If unable to read interface version, then become backwards compatible.
+    int32_t version = 1;
+    const auto status = mAidlComposerClient->getInterfaceVersion(&version);
+    if (!status.isOk()) {
+        ALOGE("getInterfaceVersion for AidlComposer constructor failed %s",
+              status.getDescription().c_str());
+    }
+    if (version == 1) {
+        mClearSlotBuffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+                                                   GraphicBuffer::USAGE_HW_COMPOSER |
+                                                           GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                                           GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+                                                   "AidlComposer");
+        if (!mClearSlotBuffer || mClearSlotBuffer->initCheck() != ::android::OK) {
+            LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+            return;
+        }
+    }
+
     ALOGI("Loaded AIDL composer3 HAL service");
 }
 
@@ -297,12 +329,11 @@
     }
 }
 
-void AidlComposer::resetCommands() {
-    mWriter.reset();
-}
-
-Error AidlComposer::executeCommands() {
-    return execute();
+Error AidlComposer::executeCommands(Display display) {
+    mMutex.lock_shared();
+    auto error = execute(display);
+    mMutex.unlock_shared();
+    return error;
 }
 
 uint32_t AidlComposer::getMaxVirtualDisplayCount() {
@@ -333,6 +364,7 @@
 
     *outDisplay = translate<Display>(virtualDisplay.display);
     *format = static_cast<PixelFormat>(virtualDisplay.format);
+    addDisplay(translate<Display>(virtualDisplay.display));
     return Error::NONE;
 }
 
@@ -342,12 +374,20 @@
         ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str());
         return static_cast<Error>(status.getServiceSpecificError());
     }
+    removeDisplay(display);
     return Error::NONE;
 }
 
 Error AidlComposer::acceptDisplayChanges(Display display) {
-    mWriter.acceptDisplayChanges(translate<int64_t>(display));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().acceptDisplayChanges(translate<int64_t>(display));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::createLayer(Display display, Layer* outLayer) {
@@ -387,7 +427,17 @@
 Error AidlComposer::getChangedCompositionTypes(
         Display display, std::vector<Layer>* outLayers,
         std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
-    const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display));
+    std::vector<ChangedCompositionLayer> changedLayers;
+    Error error = Error::NONE;
+    {
+        mMutex.lock_shared();
+        if (auto reader = getReader(display)) {
+            changedLayers = reader->get().takeChangedCompositionTypes(translate<int64_t>(display));
+        } else {
+            error = Error::BAD_DISPLAY;
+        }
+        mMutex.unlock_shared();
+    }
     outLayers->reserve(changedLayers.size());
     outTypes->reserve(changedLayers.size());
 
@@ -395,7 +445,7 @@
         outLayers->emplace_back(translate<Layer>(layer.layer));
         outTypes->emplace_back(layer.composition);
     }
-    return Error::NONE;
+    return error;
 }
 
 Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
@@ -447,7 +497,17 @@
 Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
                                        std::vector<Layer>* outLayers,
                                        std::vector<uint32_t>* outLayerRequestMasks) {
-    const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display));
+    Error error = Error::NONE;
+    DisplayRequest displayRequests;
+    {
+        mMutex.lock_shared();
+        if (auto reader = getReader(display)) {
+            displayRequests = reader->get().takeDisplayRequests(translate<int64_t>(display));
+        } else {
+            error = Error::BAD_DISPLAY;
+        }
+        mMutex.unlock_shared();
+    }
     *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask);
     outLayers->reserve(displayRequests.layerRequests.size());
     outLayerRequestMasks->reserve(displayRequests.layerRequests.size());
@@ -456,7 +516,7 @@
         outLayers->emplace_back(translate<Layer>(layer.layer));
         outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask));
     }
-    return Error::NONE;
+    return error;
 }
 
 Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
@@ -496,16 +556,35 @@
         return static_cast<Error>(status.getServiceSpecificError());
     }
 
-    *outTypes = translate<Hdr>(capabilities.types);
+    *outTypes = capabilities.types;
     *outMaxLuminance = capabilities.maxLuminance;
     *outMaxAverageLuminance = capabilities.maxAverageLuminance;
     *outMinLuminance = capabilities.minLuminance;
     return Error::NONE;
 }
 
+Error AidlComposer::getOverlaySupport(AidlOverlayProperties* outProperties) {
+    const auto status = mAidlComposerClient->getOverlaySupport(outProperties);
+    if (!status.isOk()) {
+        ALOGE("getOverlaySupport failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
 Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
                                      std::vector<int>* outReleaseFences) {
-    auto fences = mReader.takeReleaseFences(translate<int64_t>(display));
+    Error error = Error::NONE;
+    std::vector<ReleaseFences::Layer> fences;
+    {
+        mMutex.lock_shared();
+        if (auto reader = getReader(display)) {
+            fences = reader->get().takeReleaseFences(translate<int64_t>(display));
+        } else {
+            error = Error::BAD_DISPLAY;
+        }
+        mMutex.unlock_shared();
+    }
     outLayers->reserve(fences.size());
     outReleaseFences->reserve(fences.size());
 
@@ -516,19 +595,31 @@
         *fence.fence.getR() = -1;
         outReleaseFences->emplace_back(fenceOwner);
     }
-    return Error::NONE;
+    return error;
 }
 
 Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
-    ATRACE_NAME("HwcPresentDisplay");
-    mWriter.presentDisplay(translate<int64_t>(display));
+    const auto displayId = translate<int64_t>(display);
+    ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
 
-    Error error = execute();
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    auto writer = getWriter(display);
+    auto reader = getReader(display);
+    if (writer && reader) {
+        writer->get().presentDisplay(displayId);
+        error = execute(display);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+
     if (error != Error::NONE) {
+        mMutex.unlock_shared();
         return error;
     }
 
-    auto fence = mReader.takePresentFence(translate<int64_t>(display));
+    auto fence = reader->get().takePresentFence(displayId);
+    mMutex.unlock_shared();
     // take ownership
     *outPresentFence = fence.get();
     *fence.getR() = -1;
@@ -553,11 +644,19 @@
         handle = target->getNativeBuffer()->handle;
     }
 
-    mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
-                            translate<aidl::android::hardware::graphics::common::Dataspace>(
-                                    dataspace),
-                            translate<AidlRect>(damage));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get()
+                .setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
+                                 translate<aidl::android::hardware::graphics::common::Dataspace>(
+                                         dataspace),
+                                 translate<AidlRect>(damage));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
@@ -573,14 +672,28 @@
 }
 
 Error AidlComposer::setColorTransform(Display display, const float* matrix) {
-    mWriter.setColorTransform(translate<int64_t>(display), matrix);
-    return Error::NONE;
+    auto error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setColorTransform(translate<int64_t>(display), matrix);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
                                     int releaseFence) {
-    mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
-    return Error::NONE;
+    auto error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
@@ -617,57 +730,89 @@
 
 Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
                                     uint32_t* outNumTypes, uint32_t* outNumRequests) {
-    ATRACE_NAME("HwcValidateDisplay");
-    mWriter.validateDisplay(translate<int64_t>(display),
-                            ClockMonotonicTimestamp{expectedPresentTime});
+    const auto displayId = translate<int64_t>(display);
+    ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
 
-    Error error = execute();
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    auto writer = getWriter(display);
+    auto reader = getReader(display);
+    if (writer && reader) {
+        writer->get().validateDisplay(displayId, ClockMonotonicTimestamp{expectedPresentTime});
+        error = execute(display);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+
     if (error != Error::NONE) {
+        mMutex.unlock_shared();
         return error;
     }
 
-    mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+    reader->get().hasChanges(displayId, outNumTypes, outNumRequests);
 
+    mMutex.unlock_shared();
     return Error::NONE;
 }
 
 Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
                                              uint32_t* outNumTypes, uint32_t* outNumRequests,
                                              int* outPresentFence, uint32_t* state) {
-    ATRACE_NAME("HwcPresentOrValidateDisplay");
-    mWriter.presentOrvalidateDisplay(translate<int64_t>(display),
-                                     ClockMonotonicTimestamp{expectedPresentTime});
+    const auto displayId = translate<int64_t>(display);
+    ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
 
-    Error error = execute();
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    auto writer = getWriter(display);
+    auto reader = getReader(display);
+    if (writer && reader) {
+        writer->get().presentOrvalidateDisplay(displayId,
+                                               ClockMonotonicTimestamp{expectedPresentTime});
+        error = execute(display);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+
     if (error != Error::NONE) {
+        mMutex.unlock_shared();
         return error;
     }
 
-    const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display));
+    const auto result = reader->get().takePresentOrValidateStage(displayId);
     if (!result.has_value()) {
         *state = translate<uint32_t>(-1);
+        mMutex.unlock_shared();
         return Error::NO_RESOURCES;
     }
 
     *state = translate<uint32_t>(*result);
 
     if (*result == PresentOrValidate::Result::Presented) {
-        auto fence = mReader.takePresentFence(translate<int64_t>(display));
+        auto fence = reader->get().takePresentFence(displayId);
         // take ownership
         *outPresentFence = fence.get();
         *fence.getR() = -1;
     }
 
     if (*result == PresentOrValidate::Result::Validated) {
-        mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+        reader->get().hasChanges(displayId, outNumTypes, outNumRequests);
     }
 
+    mMutex.unlock_shared();
     return Error::NONE;
 }
 
 Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
-    mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer),
+                                             x, y);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
@@ -677,90 +822,232 @@
         handle = buffer->getNativeBuffer()->handle;
     }
 
-    mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle,
-                           acquireFence);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot,
+                                     handle, acquireFence);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
+Error AidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer,
+                                               const std::vector<uint32_t>& slotsToClear,
+                                               uint32_t activeBufferSlot) {
+    if (slotsToClear.empty()) {
+        return Error::NONE;
+    }
+
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+        // buffer, using the slot that needs to cleared... tricky.
+        if (mClearSlotBuffer == nullptr) {
+            writer->get().setLayerBufferSlotsToClear(translate<int64_t>(display),
+                                                     translate<int64_t>(layer), slotsToClear);
+        } else {
+            for (uint32_t slot : slotsToClear) {
+                // Don't clear the active buffer slot because we need to restore the active buffer
+                // after clearing the requested buffer slots with a placeholder buffer.
+                if (slot != activeBufferSlot) {
+                    writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
+                                                               translate<int64_t>(layer), slot,
+                                                               mClearSlotBuffer->handle,
+                                                               /*fence*/ -1);
+                }
+            }
+            // Since we clear buffers by setting them to a placeholder buffer, we want to make
+            // sure that the last setLayerBuffer command is sent with the currently active
+            // buffer, not the placeholder buffer, so that there is no perceptual change when
+            // buffers are discarded.
+            writer->get().setLayerBufferWithNewCommand(translate<int64_t>(display),
+                                                       translate<int64_t>(layer), activeBufferSlot,
+                                                       // The active buffer is still cached in
+                                                       // its slot and doesn't need a fence.
+                                                       /*buffer*/ nullptr, /*fence*/ -1);
+        }
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
                                           const std::vector<IComposerClient::Rect>& damage) {
-    mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
-                                  translate<AidlRect>(damage));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
+                                            translate<AidlRect>(damage));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
                                       IComposerClient::BlendMode mode) {
-    mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
-                              translate<BlendMode>(mode));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
+                                        translate<BlendMode>(mode));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerColor(Display display, Layer layer, const Color& color) {
-    mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerColor(translate<int64_t>(display), translate<int64_t>(layer), color);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerCompositionType(
         Display display, Layer layer,
         aidl::android::hardware::graphics::composer3::Composition type) {
-    mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerCompositionType(translate<int64_t>(display),
+                                              translate<int64_t>(layer), type);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
-    mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
-                              translate<AidlDataspace>(dataspace));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
+                                        translate<AidlDataspace>(dataspace));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
                                          const IComposerClient::Rect& frame) {
-    mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
-                                 translate<AidlRect>(frame));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
+                                           translate<AidlRect>(frame));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
-    mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer),
+                                         alpha);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
                                            const native_handle_t* stream) {
-    mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer),
+                                             stream);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
                                        const IComposerClient::FRect& crop) {
-    mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
-                               translate<AidlFRect>(crop));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
+                                         translate<AidlFRect>(crop));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
-    mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
-                              translate<AidlTransform>(transform));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
+                                        translate<AidlTransform>(transform));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
                                           const std::vector<IComposerClient::Rect>& visible) {
-    mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
-                                  translate<AidlRect>(visible));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
+                                            translate<AidlRect>(visible));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
-    mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
-Error AidlComposer::execute() {
-    const auto& commands = mWriter.getPendingCommands();
+Error AidlComposer::execute(Display display) {
+    auto writer = getWriter(display);
+    auto reader = getReader(display);
+    if (!writer || !reader) {
+        return Error::BAD_DISPLAY;
+    }
+
+    auto commands = writer->get().takePendingCommands();
     if (commands.empty()) {
-        mWriter.reset();
         return Error::NONE;
     }
 
@@ -772,9 +1059,9 @@
             return static_cast<Error>(status.getServiceSpecificError());
         }
 
-        mReader.parse(std::move(results));
+        reader->get().parse(std::move(results));
     }
-    const auto commandErrors = mReader.takeErrors();
+    const auto commandErrors = reader->get().takeErrors();
     Error error = Error::NONE;
     for (const auto& cmdErr : commandErrors) {
         const auto index = static_cast<size_t>(cmdErr.commandIndex);
@@ -792,17 +1079,23 @@
         }
     }
 
-    mWriter.reset();
-
     return error;
 }
 
 Error AidlComposer::setLayerPerFrameMetadata(
         Display display, Layer layer,
         const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
-    mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer),
-                                     translate<AidlPerFrameMetadata>(perFrameMetadatas));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerPerFrameMetadata(translate<int64_t>(display),
+                                               translate<int64_t>(layer),
+                                               translate<AidlPerFrameMetadata>(perFrameMetadatas));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys(
@@ -862,8 +1155,16 @@
 }
 
 Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
-    mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix);
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer),
+                                             matrix);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
@@ -926,20 +1227,36 @@
 Error AidlComposer::setLayerPerFrameMetadataBlobs(
         Display display, Layer layer,
         const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
-    mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer),
-                                          translate<AidlPerFrameMetadataBlob>(metadata));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerPerFrameMetadataBlobs(translate<int64_t>(display),
+                                                    translate<int64_t>(layer),
+                                                    translate<AidlPerFrameMetadataBlob>(metadata));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::setDisplayBrightness(Display display, float brightness, float brightnessNits,
                                          const DisplayBrightnessOptions& options) {
-    mWriter.setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits);
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setDisplayBrightness(translate<int64_t>(display), brightness, brightnessNits);
 
-    if (options.applyImmediately) {
-        return execute();
+        if (options.applyImmediately) {
+            error = execute(display);
+            mMutex.unlock_shared();
+            return error;
+        }
+    } else {
+        error = Error::BAD_DISPLAY;
     }
-
-    return Error::NONE;
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::getDisplayCapabilities(Display display,
@@ -1077,22 +1394,81 @@
     return Error::NONE;
 }
 
-Error AidlComposer::getClientTargetProperty(
-        Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
-    *outClientTargetProperty = mReader.takeClientTargetProperty(translate<int64_t>(display));
+Error AidlComposer::getHdrConversionCapabilities(
+        std::vector<AidlHdrConversionCapability>* hdrConversionCapabilities) {
+    const auto status =
+            mAidlComposerClient->getHdrConversionCapabilities(hdrConversionCapabilities);
+    if (!status.isOk()) {
+        hdrConversionCapabilities = {};
+        ALOGE("getHdrConversionCapabilities failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
     return Error::NONE;
 }
 
-Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
-    mWriter.setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer), brightness);
+Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConversionStrategy,
+                                             Hdr* outPreferredHdrOutputType) {
+    const auto status = mAidlComposerClient->setHdrConversionStrategy(hdrConversionStrategy,
+                                                                      outPreferredHdrOutputType);
+    if (!status.isOk()) {
+        ALOGE("setHdrConversionStrategy failed %s", status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
     return Error::NONE;
 }
 
+Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) {
+    const auto status =
+            mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>(
+                                                                                   displayId),
+                                                                           enabled);
+    if (!status.isOk()) {
+        ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s",
+              status.getDescription().c_str());
+        return static_cast<Error>(status.getServiceSpecificError());
+    }
+    return Error::NONE;
+}
+
+Error AidlComposer::getClientTargetProperty(
+        Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto reader = getReader(display)) {
+        *outClientTargetProperty =
+                reader->get().takeClientTargetProperty(translate<int64_t>(display));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
+Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerBrightness(translate<int64_t>(display), translate<int64_t>(layer),
+                                         brightness);
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
+}
+
 Error AidlComposer::setLayerBlockingRegion(Display display, Layer layer,
                                            const std::vector<IComposerClient::Rect>& blocking) {
-    mWriter.setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
-                                   translate<AidlRect>(blocking));
-    return Error::NONE;
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto writer = getWriter(display)) {
+        writer->get().setLayerBlockingRegion(translate<int64_t>(display), translate<int64_t>(layer),
+                                             translate<AidlRect>(blocking));
+    } else {
+        error = Error::BAD_DISPLAY;
+    }
+    mMutex.unlock_shared();
+    return error;
 }
 
 Error AidlComposer::getDisplayDecorationSupport(Display display,
@@ -1130,5 +1506,94 @@
     return Error::NONE;
 }
 
+ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display)
+        REQUIRES_SHARED(mMutex) {
+    return mWriters.get(display);
+}
+
+ftl::Optional<std::reference_wrapper<ComposerClientReader>> AidlComposer::getReader(Display display)
+        REQUIRES_SHARED(mMutex) {
+    if (mSingleReader) {
+        display = translate<Display>(kSingleReaderKey);
+    }
+    return mReaders.get(display);
+}
+
+void AidlComposer::removeDisplay(Display display) {
+    mMutex.lock();
+    bool wasErased = mWriters.erase(display);
+    ALOGW_IF(!wasErased,
+             "Attempting to remove writer for display %" PRId64 " which is not connected",
+             translate<int64_t>(display));
+    if (!mSingleReader) {
+        removeReader(display);
+    }
+    mMutex.unlock();
+}
+
+void AidlComposer::onHotplugDisconnect(Display display) {
+    removeDisplay(display);
+}
+
+bool AidlComposer::hasMultiThreadedPresentSupport(Display display) {
+#if 0
+    // TODO (b/259132483): Reenable
+    const auto displayId = translate<int64_t>(display);
+    std::vector<AidlDisplayCapability> capabilities;
+    const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities);
+    if (!status.isOk()) {
+        ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+        return false;
+    }
+    return std::find(capabilities.begin(), capabilities.end(),
+                     AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end();
+#else
+    (void) display;
+    return false;
+#endif
+}
+
+void AidlComposer::addReader(Display display) {
+    const auto displayId = translate<int64_t>(display);
+    std::optional<int64_t> displayOpt;
+    if (displayId != kSingleReaderKey) {
+        displayOpt.emplace(displayId);
+    }
+    auto [it, added] = mReaders.try_emplace(display, std::move(displayOpt));
+    ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
+             displayId);
+}
+
+void AidlComposer::removeReader(Display display) {
+    bool wasErased = mReaders.erase(display);
+    ALOGW_IF(!wasErased,
+             "Attempting to remove reader for display %" PRId64 " which is not connected",
+             translate<int64_t>(display));
+}
+
+void AidlComposer::addDisplay(Display display) {
+    const auto displayId = translate<int64_t>(display);
+    mMutex.lock();
+    auto [it, added] = mWriters.try_emplace(display, displayId);
+    ALOGW_IF(!added, "Attempting to add writer for display %" PRId64 " which is already connected",
+             displayId);
+    if (mSingleReader) {
+        if (hasMultiThreadedPresentSupport(display)) {
+            mSingleReader = false;
+            removeReader(translate<Display>(kSingleReaderKey));
+            // Note that this includes the new display.
+            for (const auto& [existingDisplay, _] : mWriters) {
+                addReader(existingDisplay);
+            }
+        }
+    } else {
+        addReader(display);
+    }
+    mMutex.unlock();
+}
+
+void AidlComposer::onHotplugConnect(Display display) {
+    addDisplay(display);
+}
 } // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 18d2242..ce05b38 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -17,10 +17,12 @@
 #pragma once
 
 #include "ComposerHal.h"
+#include <ftl/shared_mutex.h>
+#include <ftl/small_map.h>
 
+#include <functional>
 #include <optional>
 #include <string>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -46,8 +48,11 @@
 namespace android::Hwc2 {
 
 using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+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::OverlayProperties;
 
 class AidlIComposerCallbackWrapper;
 
@@ -67,12 +72,8 @@
 
     void registerCallback(HWC2::ComposerCallback& callback) override;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    void resetCommands() override;
-
     // Explicitly flush all pending commands in the command buffer.
-    Error executeCommands() override;
+    Error executeCommands(Display) override;
 
     uint32_t getMaxVirtualDisplayCount() override;
     Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
@@ -101,8 +102,9 @@
 
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
+    Error getOverlaySupport(OverlayProperties* outProperties) override;
 
     Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
                            std::vector<int>* outReleaseFences) override;
@@ -139,6 +141,9 @@
     /* see setClientTarget for the purpose of slot */
     Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
                          const sp<GraphicBuffer>& buffer, int acquireFence) override;
+    Error setLayerBufferSlotsToClear(Display display, Layer layer,
+                                     const std::vector<uint32_t>& slotsToClear,
+                                     uint32_t activeBufferSlot) override;
     Error setLayerSurfaceDamage(Display display, Layer layer,
                                 const std::vector<IComposerClient::Rect>& damage) override;
     Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
@@ -226,16 +231,32 @@
 
     Error getPhysicalDisplayOrientation(Display displayId,
                                         AidlTransform* outDisplayOrientation) override;
+    void onHotplugConnect(Display) override;
+    void onHotplugDisconnect(Display) override;
+    Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override;
+    Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override;
+    Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
 
 private:
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
     // this function to execute the command queue.
-    Error execute();
+    Error execute(Display) REQUIRES_SHARED(mMutex);
 
     // returns the default instance name for the given service
     static std::string instance(const std::string& serviceName);
 
+    ftl::Optional<std::reference_wrapper<ComposerClientWriter>> getWriter(Display)
+            REQUIRES_SHARED(mMutex);
+    ftl::Optional<std::reference_wrapper<ComposerClientReader>> getReader(Display)
+            REQUIRES_SHARED(mMutex);
+    void addDisplay(Display) EXCLUDES(mMutex);
+    void removeDisplay(Display) EXCLUDES(mMutex);
+    void addReader(Display) REQUIRES(mMutex);
+    void removeReader(Display) REQUIRES(mMutex);
+
+    bool hasMultiThreadedPresentSupport(Display);
+
     // 64KiB minus a small space for metadata such as read/write pointers
     static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
     // Max number of buffers that may be cached for a given layer
@@ -243,8 +264,28 @@
     // 1. Tightly coupling this cache to the max size of BufferQueue
     // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
     static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
-    ComposerClientWriter mWriter;
-    ComposerClientReader mReader;
+
+    // Without DisplayCapability::MULTI_THREADED_PRESENT, we use a single reader
+    // for all displays. With the capability, we use a separate reader for each
+    // display.
+    bool mSingleReader = true;
+    // Invalid displayId used as a key to mReaders when mSingleReader is true.
+    static constexpr int64_t kSingleReaderKey = 0;
+
+    // TODO (b/256881188): Use display::PhysicalDisplayMap instead of hard-coded `3`
+    ftl::SmallMap<Display, ComposerClientWriter, 3> mWriters GUARDED_BY(mMutex);
+    ftl::SmallMap<Display, ComposerClientReader, 3> mReaders GUARDED_BY(mMutex);
+    // Protect access to mWriters and mReaders with a shared_mutex. Adding and
+    // removing a display require exclusive access, since the iterator or the
+    // writer/reader may be invalidated. Other calls need shared access while
+    // using the writer/reader, so they can use their display's writer/reader
+    // without it being deleted or the iterator being invalidated.
+    // TODO (b/257958323): Use std::shared_mutex and RAII once they support
+    // threading annotations.
+    ftl::SharedMutex mMutex;
+
+    // Buffer slots for layers are cleared by setting the slot buffer to this buffer.
+    sp<GraphicBuffer> mClearSlotBuffer;
 
     // Aidl interface
     using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index d266d94..cf67795 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -32,12 +32,15 @@
 #include <utils/StrongPointer.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/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #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/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
 #include <aidl/android/hardware/graphics/common/Transform.h>
 #include <optional>
@@ -65,7 +68,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -83,6 +85,7 @@
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
 using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
 using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
 
 class Composer {
 public:
@@ -107,12 +110,8 @@
 
     virtual void registerCallback(HWC2::ComposerCallback& callback) = 0;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    virtual void resetCommands() = 0;
-
     // Explicitly flush all pending commands in the command buffer.
-    virtual Error executeCommands() = 0;
+    virtual Error executeCommands(Display) = 0;
 
     virtual uint32_t getMaxVirtualDisplayCount() = 0;
     virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat*,
@@ -139,7 +138,7 @@
 
     virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
     virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
-    virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+    virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                      float* outMaxLuminance, float* outMaxAverageLuminance,
                                      float* outMinLuminance) = 0;
 
@@ -178,6 +177,9 @@
     /* see setClientTarget for the purpose of slot */
     virtual Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
                                  const sp<GraphicBuffer>& buffer, int acquireFence) = 0;
+    virtual Error setLayerBufferSlotsToClear(Display display, Layer layer,
+                                             const std::vector<uint32_t>& slotsToClear,
+                                             uint32_t activeBufferSlot) = 0;
     virtual Error setLayerSurfaceDamage(Display display, Layer layer,
                                         const std::vector<IComposerClient::Rect>& damage) = 0;
     virtual Error setLayerBlendMode(Display display, Layer layer,
@@ -281,6 +283,14 @@
     virtual Error setIdleTimerEnabled(Display displayId, std::chrono::milliseconds timeout) = 0;
     virtual Error getPhysicalDisplayOrientation(Display displayId,
                                                 AidlTransform* outDisplayOrientation) = 0;
+    virtual Error getOverlaySupport(V3_0::OverlayProperties* outProperties) = 0;
+    virtual void onHotplugConnect(Display) = 0;
+    virtual void onHotplugDisconnect(Display) = 0;
+    virtual Error getHdrConversionCapabilities(
+            std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0;
+    virtual Error setHdrConversionStrategy(
+            ::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0;
+    virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index eb14933..ce602a8 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@
     mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
     mConsumer->setMaxAcquiredBufferCount(
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
+
+    for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+        mHwcBufferIds[i] = UINT64_MAX;
+    }
 }
 
 void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
@@ -88,31 +92,16 @@
 }
 
 status_t FramebufferSurface::advanceFrame() {
-    uint32_t slot = 0;
-    sp<GraphicBuffer> buf;
-    sp<Fence> acquireFence(Fence::NO_FENCE);
-    Dataspace dataspace = Dataspace::UNKNOWN;
-    status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
-    mDataSpace = dataspace;
-    if (result != NO_ERROR) {
-        ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
-                strerror(-result), result);
-    }
-    return result;
-}
-
-status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
-        sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
-        Dataspace& outDataspace) {
     Mutex::Autolock lock(mMutex);
 
     BufferItem item;
     status_t err = acquireBufferLocked(&item, 0);
     if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
-        mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
+        mDataspace = Dataspace::UNKNOWN;
         return NO_ERROR;
     } else if (err != NO_ERROR) {
         ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+        mDataspace = Dataspace::UNKNOWN;
         return err;
     }
 
@@ -133,13 +122,18 @@
     mCurrentBufferSlot = item.mSlot;
     mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
     mCurrentFence = item.mFence;
+    mDataspace = static_cast<Dataspace>(item.mDataSpace);
 
-    outFence = item.mFence;
-    mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
-    outDataspace = static_cast<Dataspace>(item.mDataSpace);
-    status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
+    // assume HWC has previously seen the buffer in this slot
+    sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+    if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) {
+        mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId();
+        hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
+    }
+    status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
+                                           mDataspace);
     if (result != NO_ERROR) {
-        ALOGE("error posting framebuffer: %d", result);
+        ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
         return result;
     }
 
@@ -190,7 +184,7 @@
         limitedSize.width = maxSize.height * aspectRatio;
         wasLimited = true;
     }
-    ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+    ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]",
              limitedSize.width, limitedSize.height, size.width, size.height);
     return limitedSize;
 }
@@ -198,9 +192,9 @@
 void FramebufferSurface::dumpAsString(String8& result) const {
     Mutex::Autolock lock(mMutex);
     result.append("   FramebufferSurface\n");
-    result.appendFormat("      mDataSpace=%s (%d)\n",
-                        dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
-                        mDataSpace);
+    result.appendFormat("      mDataspace=%s (%d)\n",
+                        dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(),
+                        mDataspace);
     ConsumerBase::dumpLocked(result, "      ");
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index d41a856..0b863da 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,7 +21,7 @@
 #include <sys/types.h>
 
 #include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
 #include <gui/ConsumerBase.h>
 #include <ui/DisplayId.h>
 #include <ui/Size.h>
@@ -69,12 +69,6 @@
 
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
-    // nextBuffer waits for and then latches the next buffer from the
-    // BufferQueue and releases the previously latched buffer to the
-    // BufferQueue.  The new buffer is returned in the 'buffer' argument.
-    status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
-            sp<Fence>& outFence, ui::Dataspace& outDataspace);
-
     const PhysicalDisplayId mDisplayId;
 
     // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
@@ -91,7 +85,7 @@
     // compositing. Otherwise it will display the dataspace of the buffer
     // use for compositing which can change as wide-color content is
     // on/off.
-    ui::Dataspace mDataSpace;
+    ui::Dataspace mDataspace;
 
     // mCurrentBuffer is the current buffer or nullptr to indicate that there is
     // no current buffer.
@@ -103,7 +97,9 @@
     // Hardware composer, owned by SurfaceFlinger.
     HWComposer& mHwc;
 
-    compositionengine::impl::HwcBufferCache mHwcBufferCache;
+    // Buffers that HWC has seen before, indexed by slot number.
+    // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+    uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
 
     // Previous buffer to release after getting an updated retire fence
     bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8364ed9..aaf2523 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -40,6 +40,7 @@
 using aidl::android::hardware::graphics::composer3::Composition;
 using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
 
@@ -319,20 +320,25 @@
     float maxLuminance = -1.0f;
     float maxAverageLuminance = -1.0f;
     float minLuminance = -1.0f;
-    std::vector<Hwc2::Hdr> types;
-    auto intError = mComposer.getHdrCapabilities(mId, &types,
-            &maxLuminance, &maxAverageLuminance, &minLuminance);
+    std::vector<Hwc2::Hdr> hdrTypes;
+    auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+                                                 &maxAverageLuminance, &minLuminance);
     auto error = static_cast<HWC2::Error>(intError);
 
     if (error != Error::NONE) {
         return error;
     }
 
-    *outCapabilities = HdrCapabilities(std::move(types),
-            maxLuminance, maxAverageLuminance, minLuminance);
+    *outCapabilities =
+            HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
     return Error::NONE;
 }
 
+Error Display::getOverlaySupport(OverlayProperties* outProperties) const {
+    auto intError = mComposer.getOverlaySupport(outProperties);
+    return static_cast<Error>(intError);
+}
+
 Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
                                                      Dataspace* outDataspace,
                                                      uint8_t* outComponentMask) const {
@@ -369,7 +375,7 @@
     for (uint32_t element = 0; element < numElements; ++element) {
         auto layer = getLayerById(layerIds[element]);
         if (layer) {
-            sp<Fence> fence(new Fence(fenceFds[element]));
+            sp<Fence> fence(sp<Fence>::make(fenceFds[element]));
             releaseFences.emplace(layer.get(), fence);
         } else {
             ALOGE("getReleaseFences: invalid layer %" PRIu64
@@ -394,7 +400,7 @@
         return error;
     }
 
-    *outPresentFence = new Fence(presentFenceFd);
+    *outPresentFence = sp<Fence>::make(presentFenceFd);
     return Error::NONE;
 }
 
@@ -532,7 +538,7 @@
     }
 
     if (*state == 1) {
-        *outPresentFence = new Fence(presentFenceFd);
+        *outPresentFence = sp<Fence>::make(presentFenceFd);
     }
 
     if (*state == 0) {
@@ -711,6 +717,16 @@
     return static_cast<Error>(intError);
 }
 
+Error Layer::setBufferSlotsToClear(const std::vector<uint32_t>& slotsToClear,
+                                   uint32_t activeBufferSlot) {
+    if (CC_UNLIKELY(!mDisplay)) {
+        return Error::BAD_DISPLAY;
+    }
+    auto intError = mComposer.setLayerBufferSlotsToClear(mDisplay->getId(), mId, slotsToClear,
+                                                         activeBufferSlot);
+    return static_cast<Error>(intError);
+}
+
 Error Layer::setSurfaceDamage(const Region& damage)
 {
     if (CC_UNLIKELY(!mDisplay)) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 24aef9b..23dd3e5 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -43,6 +43,8 @@
 #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/OverlayProperties.h>
+#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 
 namespace android {
 
@@ -62,6 +64,8 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+
 // Implement this interface to receive hardware composer events.
 //
 // These callback functions will generally be called on a hwbinder thread, but
@@ -70,12 +74,13 @@
 struct ComposerCallback {
     virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0;
     virtual void onComposerHalRefresh(hal::HWDisplayId) = 0;
-    virtual void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                                     std::optional<hal::VsyncPeriodNanos>) = 0;
     virtual void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId,
                                                        const hal::VsyncPeriodChangeTimeline&) = 0;
     virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
     virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0;
+    virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0;
 
 protected:
     ~ComposerCallback() = default;
@@ -88,7 +93,7 @@
 
     virtual hal::HWDisplayId getId() const = 0;
     virtual bool isConnected() const = 0;
-    virtual void setConnected(bool connected) = 0; // For use by Device only
+    virtual void setConnected(bool connected) = 0; // For use by HWComposer only
     virtual bool hasCapability(
             aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
     virtual bool isVsyncPeriodSwitchSupported() const = 0;
@@ -117,6 +122,9 @@
     [[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
     [[nodiscard]] virtual hal::Error getHdrCapabilities(
             android::HdrCapabilities* outCapabilities) const = 0;
+    [[nodiscard]] virtual hal::Error getOverlaySupport(
+            aidl::android::hardware::graphics::composer3::OverlayProperties* outProperties)
+            const = 0;
     [[nodiscard]] virtual hal::Error getDisplayedContentSamplingAttributes(
             hal::PixelFormat* outFormat, hal::Dataspace* outDataspace,
             uint8_t* outComponentMask) const = 0;
@@ -204,6 +212,8 @@
     hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
     hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
     hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+    hal::Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+                                         outProperties) const override;
     hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
                                                      hal::Dataspace* outDataspace,
                                                      uint8_t* outComponentMask) const override;
@@ -253,7 +263,7 @@
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
-    void setConnected(bool connected) override; // For use by Device only
+    void setConnected(bool connected) override;
     bool hasCapability(aidl::android::hardware::graphics::composer3::DisplayCapability)
             const override EXCLUDES(mDisplayCapabilitiesMutex);
     bool isVsyncPeriodSwitchSupported() const override;
@@ -271,7 +281,7 @@
 
     // Member variables
 
-    // These are references to data owned by HWC2::Device, which will outlive
+    // These are references to data owned by HWComposer, which will outlive
     // this HWC2::Display, so these references are guaranteed to be valid for
     // the lifetime of this object.
     android::Hwc2::Composer& mComposer;
@@ -304,6 +314,8 @@
     [[nodiscard]] virtual hal::Error setBuffer(uint32_t slot,
                                                const android::sp<android::GraphicBuffer>& buffer,
                                                const android::sp<android::Fence>& acquireFence) = 0;
+    [[nodiscard]] virtual hal::Error setBufferSlotsToClear(
+            const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot) = 0;
     [[nodiscard]] virtual hal::Error setSurfaceDamage(const android::Region& damage) = 0;
 
     [[nodiscard]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
@@ -354,6 +366,8 @@
     hal::Error setCursorPosition(int32_t x, int32_t y) override;
     hal::Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
                          const android::sp<android::Fence>& acquireFence) override;
+    hal::Error setBufferSlotsToClear(const std::vector<uint32_t>& slotsToClear,
+                                     uint32_t activeBufferSlot) override;
     hal::Error setSurfaceDamage(const android::Region& damage) override;
 
     hal::Error setBlendMode(hal::BlendMode mode) override;
@@ -383,7 +397,7 @@
     hal::Error setBlockingRegion(const android::Region& region) override;
 
 private:
-    // These are references to data owned by HWC2::Device, which will outlive
+    // These are references to data owned by HWComposer, which will outlive
     // this HWC2::Layer, so these references are guaranteed to be valid for
     // the lifetime of this object.
     android::Hwc2::Composer& mComposer;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a6aee1f..f350eba 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -30,6 +30,7 @@
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <ftl/concat.h>
 #include <log/log.h>
 #include <ui/DebugUtils.h>
 #include <ui/GraphicBuffer.h>
@@ -70,6 +71,8 @@
 #define RETURN_IF_HWC_ERROR(error, displayId, ...) \
     RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
 
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
 namespace hal = android::hardware::graphics::composer::hal;
@@ -96,6 +99,8 @@
 void HWComposer::setCallback(HWC2::ComposerCallback& callback) {
     loadCapabilities();
     loadLayerMetadataSupport();
+    loadOverlayProperties();
+    loadHdrConversionCapabilities();
 
     if (mRegisteredCallback) {
         ALOGW("Callback already registered. Ignored extra registration attempt.");
@@ -144,36 +149,37 @@
     return mUpdateDeviceProductInfoOnHotplugReconnect;
 }
 
-bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
-    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
-    if (!displayId) {
+std::optional<PhysicalDisplayId> HWComposer::onVsync(hal::HWDisplayId hwcDisplayId,
+                                                     nsecs_t timestamp) {
+    const auto displayIdOpt = toPhysicalDisplayId(hwcDisplayId);
+    if (!displayIdOpt) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
-        return false;
+        return {};
     }
 
-    RETURN_IF_INVALID_DISPLAY(*displayId, false);
+    RETURN_IF_INVALID_DISPLAY(*displayIdOpt, {});
 
-    auto& displayData = mDisplayData[*displayId];
+    auto& displayData = mDisplayData[*displayIdOpt];
 
     {
         // There have been reports of HWCs that signal several vsync events
         // with the same timestamp when turning the display off and on. This
         // is a bug in the HWC implementation, but filter the extra events
         // out here so they don't cause havoc downstream.
-        if (timestamp == displayData.lastHwVsync) {
+        if (timestamp == displayData.lastPresentTimestamp) {
             ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
-                  to_string(*displayId).c_str(), timestamp);
-            return false;
+                  to_string(*displayIdOpt).c_str(), timestamp);
+            return {};
         }
 
-        displayData.lastHwVsync = timestamp;
+        displayData.lastPresentTimestamp = timestamp;
     }
 
-    const auto tag = "HW_VSYNC_" + to_string(*displayId);
-    ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
+    ATRACE_INT(ftl::Concat("HW_VSYNC_", displayIdOpt->value).c_str(),
+               displayData.vsyncTraceToggle);
     displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
 
-    return true;
+    return displayIdOpt;
 }
 
 size_t HWComposer::getMaxVirtualDisplayCount() const {
@@ -252,11 +258,7 @@
 }
 
 bool HWComposer::isConnected(PhysicalDisplayId displayId) const {
-    if (mDisplayData.count(displayId)) {
-        return mDisplayData.at(displayId).hwcDisplay->isConnected();
-    }
-
-    return false;
+    return mDisplayData.count(displayId) && mDisplayData.at(displayId).hwcDisplay->isConnected();
 }
 
 std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId displayId) const {
@@ -286,17 +288,12 @@
 
 std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt);
-
     const auto hwcId = *fromPhysicalDisplayId(displayId);
-    ALOGV("[%" PRIu64 "] getActiveMode", hwcId);
+
     hal::HWConfigId configId;
-    auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
+    const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId));
 
-    if (error == hal::Error::BAD_CONFIG) {
-        LOG_DISPLAY_ERROR(displayId, "No active mode");
-        return std::nullopt;
-    }
-
+    RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt);
     return configId;
 }
 
@@ -380,8 +377,8 @@
 
     displayData.vsyncEnabled = enabled;
 
-    const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
-    ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
+    ATRACE_INT(ftl::Concat("HW_VSYNC_ON_", displayId.value).c_str(),
+               enabled == hal::Vsync::ENABLE ? 1 : 0);
 }
 
 status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot,
@@ -398,8 +395,8 @@
 
 status_t HWComposer::getDeviceCompositionChanges(
         HalDisplayId displayId, bool frameUsesClientComposition,
-        std::chrono::steady_clock::time_point earliestPresentTime,
-        const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
+        std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
+        nsecs_t expectedPresentTime,
         std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
@@ -429,14 +426,13 @@
 
         // If composer supports getting the expected present time, we can skip
         // as composer will make sure to prevent early presentation
-        if (mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) {
+        if (!earliestPresentTime) {
             return true;
         }
 
         // composer doesn't support getting the expected present time. We can only
         // skip validate if we know that we are not going to present early.
-        return std::chrono::steady_clock::now() >= earliestPresentTime ||
-                previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
+        return std::chrono::steady_clock::now() >= *earliestPresentTime;
     }();
 
     displayData.validateWasSkipped = false;
@@ -494,6 +490,11 @@
     return mDisplayData.at(displayId).lastPresentFence;
 }
 
+nsecs_t HWComposer::getPresentTimestamp(PhysicalDisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+    return mDisplayData.at(displayId).lastPresentTimestamp;
+}
+
 sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     const auto& displayFences = mDisplayData.at(displayId).releaseFences;
@@ -506,8 +507,8 @@
 }
 
 status_t HWComposer::presentAndGetReleaseFences(
-        HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime,
-        const std::shared_ptr<FenceTime>& previousPresentFence) {
+        HalDisplayId displayId,
+        std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -517,19 +518,15 @@
 
     if (displayData.validateWasSkipped) {
         // explicitly flush all pending commands
-        auto error = static_cast<hal::Error>(mComposer->executeCommands());
+        auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId()));
         RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
         RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
         return NO_ERROR;
     }
 
-    const bool waitForEarliestPresent =
-            !mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
-            previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
-
-    if (waitForEarliestPresent) {
+    if (earliestPresentTime) {
         ATRACE_NAME("wait for earliest present time");
-        std::this_thread::sleep_until(earliestPresentTime);
+        std::this_thread::sleep_until(*earliestPresentTime);
     }
 
     auto error = hwcDisplay->present(&displayData.lastPresentFence);
@@ -656,6 +653,11 @@
     return NO_ERROR;
 }
 
+const aidl::android::hardware::graphics::composer3::OverlayProperties&
+HWComposer::getOverlaySupport() const {
+    return mOverlayProperties;
+}
+
 int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, 0);
     return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata();
@@ -785,6 +787,37 @@
     return displayModeId;
 }
 
+std::vector<HdrConversionCapability> HWComposer::getHdrConversionCapabilities() const {
+    return mHdrConversionCapabilities;
+}
+
+status_t HWComposer::setHdrConversionStrategy(
+        HdrConversionStrategy hdrConversionStrategy,
+        aidl::android::hardware::graphics::common::Hdr* outPreferredHdrOutputType) {
+    const auto error =
+            mComposer->setHdrConversionStrategy(hdrConversionStrategy, outPreferredHdrOutputType);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in setting HDR conversion strategy %s", to_string(error).c_str());
+        return INVALID_OPERATION;
+    }
+    return NO_ERROR;
+}
+
+status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId,
+                                                               bool enabled) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId]
+                                                                         .hwcDisplay->getId(),
+                                                                 enabled);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in setting refresh refresh rate change callback debug enabled %s",
+              to_string(error).c_str());
+        return INVALID_OPERATION;
+    }
+    return NO_ERROR;
+}
+
 status_t HWComposer::getDisplayDecorationSupport(
         PhysicalDisplayId displayId,
         std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
@@ -932,6 +965,8 @@
                                                                : "Secondary display",
                                              .deviceProductInfo = std::nullopt};
         }();
+
+        mComposer->onHotplugConnect(hwcDisplayId);
     }
 
     if (!isConnected(info->id)) {
@@ -951,18 +986,16 @@
         return {};
     }
 
-    // The display will later be destroyed by a call to
-    // destroyDisplay(). For now we just mark it disconnected.
-    if (isConnected(*displayId)) {
-        mDisplayData[*displayId].hwcDisplay->setConnected(false);
-    } else {
+    if (!isConnected(*displayId)) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
+        return {};
     }
-    // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
-    // via SurfaceFlinger's onHotplugReceived callback handling
-    return DisplayIdentificationInfo{.id = *displayId,
-                                     .name = std::string(),
-                                     .deviceProductInfo = std::nullopt};
+
+    // The display will later be destroyed by a call to HWComposer::disconnectDisplay. For now, mark
+    // it as disconnected.
+    mDisplayData.at(*displayId).hwcDisplay->setConnected(false);
+    mComposer->onHotplugDisconnect(hwcDisplayId);
+    return DisplayIdentificationInfo{.id = *displayId};
 }
 
 void HWComposer::loadCapabilities() {
@@ -973,6 +1006,18 @@
     }
 }
 
+void HWComposer::loadOverlayProperties() {
+    mComposer->getOverlaySupport(&mOverlayProperties);
+}
+
+void HWComposer::loadHdrConversionCapabilities() {
+    const auto error = mComposer->getHdrConversionCapabilities(&mHdrConversionCapabilities);
+    if (error != hal::Error::NONE) {
+        ALOGE("Error in fetching HDR conversion capabilities %s", to_string(error).c_str());
+        mHdrConversionCapabilities = {};
+    }
+}
+
 status_t HWComposer::setIdleTimerEnabled(PhysicalDisplayId displayId,
                                          std::chrono::milliseconds timeout) {
     ATRACE_CALL();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 92a8f30..3702c62 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -44,10 +44,14 @@
 #include "Hal.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionCapability.h>
+#include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h>
 #include <aidl/android/hardware/graphics/composer3/Capability.h>
 #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
 namespace android {
 
@@ -139,17 +143,16 @@
     // expected.
     virtual status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
-            std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
-            std::optional<DeviceRequestedChanges>* outChanges) = 0;
+            std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
+            nsecs_t expectedPresentTime, std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
                                      const sp<GraphicBuffer>& target, ui::Dataspace) = 0;
 
     // Present layers to the display and read releaseFences.
     virtual status_t presentAndGetReleaseFences(
-            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence) = 0;
+            HalDisplayId,
+            std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) = 0;
 
     // set power mode
     virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -160,8 +163,9 @@
     // reset state when a display is disconnected
     virtual void disconnectDisplay(HalDisplayId) = 0;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     virtual sp<Fence> getPresentFence(HalDisplayId) const = 0;
+    virtual nsecs_t getPresentTimestamp(PhysicalDisplayId) const = 0;
 
     // Get last release fence for the given layer
     virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0;
@@ -177,6 +181,9 @@
     // Fetches the HDR capabilities of the given display
     virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0;
 
+    virtual const aidl::android::hardware::graphics::composer3::OverlayProperties&
+    getOverlaySupport() const = 0;
+
     virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0;
 
     // Returns the available RenderIntent of the given display.
@@ -214,7 +221,10 @@
     // TODO(b/157555476): Remove when the framework has proper support for headless mode
     virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
 
-    virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
+    // Called when a vsync happens. If the vsync is valid, returns the
+    // corresponding PhysicalDisplayId. Otherwise returns nullopt.
+    virtual std::optional<PhysicalDisplayId> onVsync(hal::HWDisplayId, nsecs_t timestamp) = 0;
+
     virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
 
     virtual bool isConnected(PhysicalDisplayId) const = 0;
@@ -281,6 +291,12 @@
     virtual status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) = 0;
     virtual bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const = 0;
     virtual Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const = 0;
+    virtual std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+    getHdrConversionCapabilities() const = 0;
+    virtual status_t setHdrConversionStrategy(
+            aidl::android::hardware::graphics::common::HdrConversionStrategy,
+            aidl::android::hardware::graphics::common::Hdr*) = 0;
+    virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
 };
 
 static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
@@ -322,8 +338,8 @@
 
     status_t getDeviceCompositionChanges(
             HalDisplayId, bool frameUsesClientComposition,
-            std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
+            std::optional<std::chrono::steady_clock::time_point> earliestPresentTime,
+            nsecs_t expectedPresentTime,
             std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -331,8 +347,8 @@
 
     // Present layers to the display and read releaseFences.
     status_t presentAndGetReleaseFences(
-            HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime,
-            const std::shared_ptr<FenceTime>& previousPresentFence) override;
+            HalDisplayId,
+            std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) override;
 
     // set power mode
     status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
@@ -343,8 +359,9 @@
     // reset state when a display is disconnected
     void disconnectDisplay(HalDisplayId) override;
 
-    // get the present fence received from the last call to present.
+    // Get the present fence/timestamp received from the last call to present.
     sp<Fence> getPresentFence(HalDisplayId) const override;
+    nsecs_t getPresentTimestamp(PhysicalDisplayId) const override;
 
     // Get last release fence for the given layer
     sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override;
@@ -360,6 +377,9 @@
     // Fetches the HDR capabilities of the given display
     status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override;
 
+    const aidl::android::hardware::graphics::composer3::OverlayProperties& getOverlaySupport()
+            const override;
+
     int32_t getSupportedPerFrameMetadata(HalDisplayId) const override;
 
     // Returns the available RenderIntent of the given display.
@@ -387,7 +407,7 @@
 
     bool updatesDeviceProductInfoOnHotplugReconnect() const override;
 
-    bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
+    std::optional<PhysicalDisplayId> onVsync(hal::HWDisplayId, nsecs_t timestamp) override;
     void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
 
     bool isConnected(PhysicalDisplayId) const override;
@@ -428,6 +448,12 @@
     status_t setIdleTimerEnabled(PhysicalDisplayId, std::chrono::milliseconds timeout) override;
     bool hasDisplayIdleTimerCapability(PhysicalDisplayId) const override;
     Hwc2::AidlTransform getPhysicalDisplayOrientation(PhysicalDisplayId) const override;
+    std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+    getHdrConversionCapabilities() const override;
+    status_t setHdrConversionStrategy(
+            aidl::android::hardware::graphics::common::HdrConversionStrategy,
+            aidl::android::hardware::graphics::common::Hdr*) override;
+    status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override;
 
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
@@ -456,7 +482,10 @@
 
     struct DisplayData {
         std::unique_ptr<HWC2::Display> hwcDisplay;
+
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
+        nsecs_t lastPresentTimestamp = 0;
+
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
 
         bool validateWasSkipped;
@@ -466,8 +495,6 @@
 
         std::mutex vsyncEnabledLock;
         hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
-
-        nsecs_t lastHwVsync = 0;
     };
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId);
@@ -479,11 +506,17 @@
 
     void loadCapabilities();
     void loadLayerMetadataSupport();
+    void loadOverlayProperties();
+    void loadHdrConversionCapabilities();
 
     std::unordered_map<HalDisplayId, DisplayData> mDisplayData;
 
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<aidl::android::hardware::graphics::composer3::Capability> mCapabilities;
+    aidl::android::hardware::graphics::composer3::OverlayProperties mOverlayProperties;
+    std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>
+            mHdrConversionCapabilities = {};
+
     std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
     bool mRegisteredCallback = false;
 
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 4737034..bf3089f 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
 #include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.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>
 
@@ -39,7 +40,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Error;
@@ -69,6 +69,7 @@
 using PowerMode = IComposerClient::PowerMode;
 using Vsync = IComposerClient::Vsync;
 using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
 
 } // namespace hardware::graphics::composer::hal
 
@@ -112,6 +113,8 @@
             return "Sideband";
         case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
             return "DisplayDecoration";
+        case aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR:
+            return "RefreshRateIndicator";
         default:
             return "Unknown";
     }
@@ -177,6 +180,10 @@
     return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
 }
 
+// For utils::Dumper ADL.
+namespace hardware::graphics::composer {
+namespace V2_2 {
+
 inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) {
     switch (mode) {
         case hardware::graphics::composer::hal::PowerMode::OFF:
@@ -194,6 +201,10 @@
     }
 }
 
+} // namespace V2_2
+
+namespace V2_1 {
+
 inline std::string to_string(hardware::graphics::composer::hal::Vsync vsync) {
     switch (vsync) {
         case hardware::graphics::composer::hal::Vsync::ENABLE:
@@ -205,4 +216,6 @@
     }
 }
 
+} // namespace V2_1
+} // namespace hardware::graphics::composer
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 2597ae6..e0f6c45 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -36,10 +36,13 @@
 #include <algorithm>
 #include <cinttypes>
 
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
 using aidl::android::hardware::graphics::composer3::DimmingStage;
 using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
 
@@ -185,9 +188,22 @@
     return out;
 }
 
+sp<GraphicBuffer> allocateClearSlotBuffer() {
+    sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBX_8888,
+                                                       GraphicBuffer::USAGE_HW_COMPOSER |
+                                                               GraphicBuffer::USAGE_SW_READ_OFTEN |
+                                                               GraphicBuffer::USAGE_SW_WRITE_OFTEN,
+                                                       "HidlComposer");
+    if (!buffer || buffer->initCheck() != ::android::OK) {
+        return nullptr;
+    }
+    return std::move(buffer);
+}
+
 } // anonymous namespace
 
-HidlComposer::HidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
+HidlComposer::HidlComposer(const std::string& serviceName)
+      : mClearSlotBuffer(allocateClearSlotBuffer()), mWriter(kWriterInitialSize) {
     mComposer = V2_1::IComposer::getService(serviceName);
 
     if (mComposer == nullptr) {
@@ -229,6 +245,11 @@
     if (mClient == nullptr) {
         LOG_ALWAYS_FATAL("failed to create composer client");
     }
+
+    if (!mClearSlotBuffer) {
+        LOG_ALWAYS_FATAL("Failed to allocate a buffer for clearing layer buffer slots");
+        return;
+    }
 }
 
 bool HidlComposer::isSupported(OptionalFeature feature) const {
@@ -272,11 +293,7 @@
     }
 }
 
-void HidlComposer::resetCommands() {
-    mWriter.reset();
-}
-
-Error HidlComposer::executeCommands() {
+Error HidlComposer::executeCommands(Display) {
     return execute();
 }
 
@@ -495,13 +512,13 @@
                      "OptionalFeature::KernelIdleTimer is not supported on HIDL");
 }
 
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
                                        float* outMaxLuminance, float* outMaxAverageLuminance,
                                        float* outMinLuminance) {
     Error error = kDefaultError;
     if (mClient_2_3) {
         mClient_2_3->getHdrCapabilities_2_3(display,
-                                            [&](const auto& tmpError, const auto& tmpTypes,
+                                            [&](const auto& tmpError, const auto& tmpHdrTypes,
                                                 const auto& tmpMaxLuminance,
                                                 const auto& tmpMaxAverageLuminance,
                                                 const auto& tmpMinLuminance) {
@@ -509,15 +526,15 @@
                                                 if (error != Error::NONE) {
                                                     return;
                                                 }
+                                                *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
 
-                                                *outTypes = tmpTypes;
                                                 *outMaxLuminance = tmpMaxLuminance;
                                                 *outMaxAverageLuminance = tmpMaxAverageLuminance;
                                                 *outMinLuminance = tmpMinLuminance;
                                             });
     } else {
         mClient->getHdrCapabilities(display,
-                                    [&](const auto& tmpError, const auto& tmpTypes,
+                                    [&](const auto& tmpError, const auto& tmpHdrTypes,
                                         const auto& tmpMaxLuminance,
                                         const auto& tmpMaxAverageLuminance,
                                         const auto& tmpMinLuminance) {
@@ -525,11 +542,7 @@
                                         if (error != Error::NONE) {
                                             return;
                                         }
-
-                                        outTypes->clear();
-                                        for (auto type : tmpTypes) {
-                                            outTypes->push_back(static_cast<Hdr>(type));
-                                        }
+                                        *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
 
                                         *outMaxLuminance = tmpMaxLuminance;
                                         *outMaxAverageLuminance = tmpMaxAverageLuminance;
@@ -540,6 +553,10 @@
     return error;
 }
 
+Error HidlComposer::getOverlaySupport(OverlayProperties* /*outProperties*/) {
+    return Error::NONE;
+}
+
 Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
                                      std::vector<int>* outReleaseFences) {
     mReader.takeReleaseFences(display, outLayers, outReleaseFences);
@@ -693,6 +710,32 @@
     return Error::NONE;
 }
 
+Error HidlComposer::setLayerBufferSlotsToClear(Display display, Layer layer,
+                                               const std::vector<uint32_t>& slotsToClear,
+                                               uint32_t activeBufferSlot) {
+    if (slotsToClear.empty()) {
+        return Error::NONE;
+    }
+    // Backwards compatible way of clearing buffer is to set the layer buffer with a placeholder
+    // buffer, using the slot that needs to cleared... tricky.
+    for (uint32_t slot : slotsToClear) {
+        // Don't clear the active buffer slot because we need to restore the active buffer after
+        // setting the requested buffer slots with a placeholder buffer.
+        if (slot != activeBufferSlot) {
+            mWriter.selectDisplay(display);
+            mWriter.selectLayer(layer);
+            mWriter.setLayerBuffer(slot, mClearSlotBuffer->handle, /*fence*/ -1);
+        }
+    }
+    // Since we clear buffers by setting them to a placeholder buffer, we want to make sure that the
+    // last setLayerBuffer command is sent with the currently active buffer, not the placeholder
+    // buffer, so that there is no perceptual change.
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerBuffer(activeBufferSlot, /*buffer*/ nullptr, /*fence*/ -1);
+    return Error::NONE;
+}
+
 Error HidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
                                           const std::vector<IComposerClient::Rect>& damage) {
     mWriter.selectDisplay(display);
@@ -1303,6 +1346,18 @@
     return Error::UNSUPPORTED;
 }
 
+Error HidlComposer::getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy, Hdr*) {
+    return Error::UNSUPPORTED;
+}
+
+Error HidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display, bool) {
+    return Error::UNSUPPORTED;
+}
+
 Error HidlComposer::getClientTargetProperty(
         Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) {
     IComposerClient::ClientTargetProperty property;
@@ -1352,6 +1407,9 @@
     registerCallback(sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
 }
 
+void HidlComposer::onHotplugConnect(Display) {}
+void HidlComposer::onHotplugDisconnect(Display) {}
+
 CommandReader::~CommandReader() {
     resetData();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d0d3c2e..0521acf 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -175,12 +174,8 @@
 
     void registerCallback(HWC2::ComposerCallback& callback) override;
 
-    // Reset all pending commands in the command buffer. Useful if you want to
-    // skip a frame but have already queued some commands.
-    void resetCommands() override;
-
     // Explicitly flush all pending commands in the command buffer.
-    Error executeCommands() override;
+    Error executeCommands(Display) override;
 
     uint32_t getMaxVirtualDisplayCount() override;
     Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
@@ -209,8 +204,10 @@
 
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+    Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
+    Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
+                                    outProperties) override;
 
     Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
                            std::vector<int>* outReleaseFences) override;
@@ -247,6 +244,9 @@
     /* see setClientTarget for the purpose of slot */
     Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
                          const sp<GraphicBuffer>& buffer, int acquireFence) override;
+    Error setLayerBufferSlotsToClear(Display display, Layer layer,
+                                     const std::vector<uint32_t>& slotsToClear,
+                                     uint32_t activeBufferSlot) override;
     Error setLayerSurfaceDamage(Display display, Layer layer,
                                 const std::vector<IComposerClient::Rect>& damage) override;
     Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
@@ -337,6 +337,14 @@
 
     Error getPhysicalDisplayOrientation(Display displayId,
                                         AidlTransform* outDisplayOrientation) override;
+    void onHotplugConnect(Display) override;
+    void onHotplugDisconnect(Display) override;
+    Error getHdrConversionCapabilities(
+            std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>*)
+            override;
+    Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                                   Hdr*) override;
+    Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
@@ -359,6 +367,9 @@
     sp<V2_3::IComposerClient> mClient_2_3;
     sp<IComposerClient> mClient_2_4;
 
+    // Buffer slots for layers are cleared by setting the slot buffer to this buffer.
+    sp<GraphicBuffer> mClearSlotBuffer;
+
     // 64KiB minus a small space for metadata such as read/write pointers
     static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
     // Max number of buffers that may be cached for a given layer
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index a0350b7..f8b466c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -31,7 +31,7 @@
 #include <utils/Mutex.h>
 #include <utils/Trace.h>
 
-#include <android/hardware/power/1.3/IPower.h>
+#include <android/hardware/power/IPower.h>
 #include <android/hardware/power/IPowerHintSession.h>
 #include <android/hardware/power/WorkDuration.h>
 
@@ -49,18 +49,12 @@
 
 namespace impl {
 
-namespace V1_0 = android::hardware::power::V1_0;
-namespace V1_3 = android::hardware::power::V1_3;
-using V1_3::PowerHint;
-
 using android::hardware::power::Boost;
-using android::hardware::power::IPower;
 using android::hardware::power::IPowerHintSession;
 using android::hardware::power::Mode;
+using android::hardware::power::SessionHint;
 using android::hardware::power::WorkDuration;
 
-using scheduler::OneShotTimer;
-
 PowerAdvisor::~PowerAdvisor() = default;
 
 namespace {
@@ -81,7 +75,8 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) : mFlinger(flinger) {
+PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
+      : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) {
     if (getUpdateTimeout() > 0ms) {
         mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(),
                                    /* resetCallback */ nullptr,
@@ -118,6 +113,10 @@
 }
 
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
+    if (!mHasExpensiveRendering) {
+        ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
+        return;
+    }
     if (expected) {
         mExpensiveDisplays.insert(displayId);
     } else {
@@ -126,23 +125,20 @@
 
     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper == nullptr) {
-            return;
-        }
-
-        if (!halWrapper->setExpensiveRendering(expectsExpensiveRendering)) {
-            // The HAL has become unavailable; attempt to reconnect later
-            mReconnectPowerHal = true;
+        auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering);
+        if (!ret.isOk()) {
+            if (ret.isUnsupported()) {
+                mHasExpensiveRendering = false;
+            }
             return;
         }
 
         mNotifiedExpensiveRendering = expectsExpensiveRendering;
+        traceExpensiveRendering(mNotifiedExpensiveRendering);
     }
 }
 
-void PowerAdvisor::notifyDisplayUpdateImminent() {
+void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
     // Only start sending this notification once the system has booted so we don't introduce an
     // early-boot dependency on Power HAL
     if (!mBootFinished.load()) {
@@ -150,16 +146,22 @@
     }
 
     if (mSendUpdateImminent.exchange(false)) {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper == nullptr) {
-            return;
+        ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
+        if (usePowerHintSession() && ensurePowerHintSessionRunning()) {
+            std::lock_guard lock(mHintSessionMutex);
+            auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
+            if (!ret.isOk()) {
+                mHintSessionRunning = false;
+            }
         }
 
-        if (!halWrapper->notifyDisplayUpdateImminent()) {
-            // The HAL has become unavailable; attempt to reconnect later
-            mReconnectPowerHal = true;
-            return;
+        if (!mHasDisplayUpdateImminent) {
+            ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
+        } else {
+            auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+            if (ret.isUnsupported()) {
+                mHasDisplayUpdateImminent = false;
+            }
         }
 
         if (mScreenUpdateTimer) {
@@ -179,88 +181,123 @@
 // checks both if it supports and if it's enabled
 bool PowerAdvisor::usePowerHintSession() {
     // uses cached value since the underlying support and flag are unlikely to change at runtime
-    return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
+    return mHintSessionEnabled.value_or(false) && supportsPowerHintSession();
 }
 
 bool PowerAdvisor::supportsPowerHintSession() {
     // cache to avoid needing lock every time
-    if (!mSupportsPowerHint.has_value()) {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession();
+    if (!mSupportsHintSession.has_value()) {
+        mSupportsHintSession = getPowerHal().getHintSessionPreferredRate().isOk();
     }
-    return *mSupportsPowerHint;
+    return *mSupportsHintSession;
 }
 
-bool PowerAdvisor::isPowerHintSessionRunning() {
-    return mPowerHintSessionRunning;
+bool PowerAdvisor::ensurePowerHintSessionRunning() {
+    if (!mHintSessionRunning && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
+        startPowerHintSession(mHintSessionThreadIds);
+    }
+    return mHintSessionRunning;
 }
 
-void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) {
+void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
     if (!usePowerHintSession()) {
         ALOGV("Power hint session target duration cannot be set, skipping");
         return;
     }
+    ATRACE_CALL();
     {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper != nullptr) {
-            halWrapper->setTargetWorkDuration(targetDuration);
+        mTargetDuration = targetDuration;
+        if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
+        if (ensurePowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
+            ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
+            mLastTargetDurationSent = targetDuration;
+            std::lock_guard lock(mHintSessionMutex);
+            auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
+            if (!ret.isOk()) {
+                ALOGW("Failed to set power hint target work duration with error: %s",
+                      ret.exceptionMessage().c_str());
+                mHintSessionRunning = false;
+            }
         }
     }
 }
 
-void PowerAdvisor::sendActualWorkDuration() {
-    if (!mBootFinished || !usePowerHintSession()) {
+void PowerAdvisor::reportActualWorkDuration() {
+    if (!mBootFinished || !sUseReportActualDuration || !usePowerHintSession()) {
         ALOGV("Actual work duration power hint cannot be sent, skipping");
         return;
     }
-    const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false);
-    if (actualDuration.has_value()) {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper != nullptr) {
-            halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(),
-                                               systemTime());
-        }
-    }
-}
-
-void PowerAdvisor::sendPredictedWorkDuration() {
-    if (!mBootFinished || !usePowerHintSession()) {
-        ALOGV("Actual work duration power hint cannot be sent, skipping");
+    ATRACE_CALL();
+    std::optional<Duration> actualDuration = estimateWorkDuration();
+    if (!actualDuration.has_value() || actualDuration < 0ns || !ensurePowerHintSessionRunning()) {
+        ALOGV("Failed to send actual work duration, skipping");
         return;
     }
+    actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
+    mActualDuration = actualDuration;
+    WorkDuration duration;
+    duration.durationNanos = actualDuration->ns();
+    duration.timeStampNanos = TimePoint::now().ns();
+    mHintSessionQueue.push_back(duration);
 
-    const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true);
+    if (sTraceHintSessionData) {
+        ATRACE_INT64("Measured duration", actualDuration->ns());
+        ATRACE_INT64("Target error term", Duration{*actualDuration - mTargetDuration}.ns());
+        ATRACE_INT64("Reported duration", actualDuration->ns());
+        ATRACE_INT64("Reported target", mLastTargetDurationSent.ns());
+        ATRACE_INT64("Reported target error term",
+                     Duration{*actualDuration - mLastTargetDurationSent}.ns());
+    }
 
-    if (predictedDuration.has_value()) {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* const halWrapper = getPowerHal();
-        if (halWrapper != nullptr) {
-            halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(),
-                                               systemTime());
+    ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
+          " with error: %" PRId64,
+          actualDuration->ns(), mLastTargetDurationSent.ns(),
+          Duration{*actualDuration - mLastTargetDurationSent}.ns());
+
+    {
+        std::lock_guard lock(mHintSessionMutex);
+        auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
+        if (!ret.isOk()) {
+            ALOGW("Failed to report actual work durations with error: %s",
+                  ret.exceptionMessage().c_str());
+            mHintSessionRunning = false;
+            return;
         }
     }
+    mHintSessionQueue.clear();
 }
 
-void PowerAdvisor::enablePowerHint(bool enabled) {
-    mPowerHintEnabled = enabled;
+void PowerAdvisor::enablePowerHintSession(bool enabled) {
+    mHintSessionEnabled = enabled;
 }
 
 bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) {
-    if (!usePowerHintSession()) {
-        ALOGI("Power hint session cannot be started, skipping");
+    if (!mBootFinished.load()) {
+        return false;
     }
+    if (!usePowerHintSession()) {
+        ALOGI("Cannot start power hint session: disabled or unsupported");
+        return false;
+    }
+    if (mHintSessionRunning) {
+        ALOGE("Cannot start power hint session: already running");
+        return false;
+    }
+    LOG_ALWAYS_FATAL_IF(threadIds.empty(), "No thread IDs provided to power hint session!");
     {
-        std::lock_guard lock(mPowerHalMutex);
-        HalWrapper* halWrapper = getPowerHal();
-        if (halWrapper != nullptr && usePowerHintSession()) {
-            halWrapper->setPowerHintSessionThreadIds(threadIds);
-            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
+        std::lock_guard lock(mHintSessionMutex);
+        mHintSession = nullptr;
+        mHintSessionThreadIds = threadIds;
+
+        auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+                                                   threadIds, mTargetDuration.ns());
+
+        if (ret.isOk()) {
+            mHintSessionRunning = true;
+            mHintSession = ret.value();
         }
     }
-    return mPowerHintSessionRunning;
+    return mHintSessionRunning;
 }
 
 void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
@@ -281,22 +318,22 @@
                 }
             }
             displayData.lastValidGpuStartTime = displayData.gpuStartTime;
-            displayData.lastValidGpuEndTime = signalTime;
+            displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime);
         }
     }
     displayData.gpuEndFenceTime = std::move(fenceTime);
-    displayData.gpuStartTime = systemTime();
+    displayData.gpuStartTime = TimePoint::now();
 }
 
-void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
-                                        nsecs_t validateEndTime) {
+void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                                        TimePoint validateEndTime) {
     DisplayTimingData& displayData = mDisplayTimingData[displayId];
     displayData.hwcValidateStartTime = validateStartTime;
     displayData.hwcValidateEndTime = validateEndTime;
 }
 
-void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                                       nsecs_t presentEndTime) {
+void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                                       TimePoint presentEndTime) {
     DisplayTimingData& displayData = mDisplayTimingData[displayId];
     displayData.hwcPresentStartTime = presentStartTime;
     displayData.hwcPresentEndTime = presentEndTime;
@@ -311,43 +348,41 @@
     mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
 }
 
-void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) {
+void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
     mExpectedPresentTimes.append(expectedPresentTime);
 }
 
-void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) {
+void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
     mLastSfPresentEndTime = presentEndTime;
     mLastPresentFenceTime = presentFenceTime;
 }
 
-void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) {
+void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) {
     mFrameDelayDuration = frameDelayDuration;
 }
 
-void PowerAdvisor::setHwcPresentDelayedTime(
-        DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) {
-    mDisplayTimingData[displayId].hwcPresentDelayedTime =
-            (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime();
+void PowerAdvisor::setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) {
+    mDisplayTimingData[displayId].hwcPresentDelayedTime = earliestFrameStartTime;
 }
 
-void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) {
+void PowerAdvisor::setCommitStart(TimePoint commitStartTime) {
     mCommitStartTimes.append(commitStartTime);
 }
 
-void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) {
-    mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime;
+void PowerAdvisor::setCompositeEnd(TimePoint compositeEndTime) {
+    mLastPostcompDuration = compositeEndTime - mLastSfPresentEndTime;
 }
 
 void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) {
     mDisplayIds = displayIds;
 }
 
-void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) {
+void PowerAdvisor::setTotalFrameTargetWorkDuration(Duration targetDuration) {
     mTotalFrameTargetDuration = targetDuration;
 }
 
 std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds(
-        std::optional<nsecs_t> DisplayTimingData::*sortBy) {
+        std::optional<TimePoint> DisplayTimingData::*sortBy) {
     std::vector<DisplayId> sortedDisplays;
     std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays),
                  [&](DisplayId id) {
@@ -360,39 +395,30 @@
     return sortedDisplays;
 }
 
-std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) {
-    if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) {
+std::optional<Duration> PowerAdvisor::estimateWorkDuration() {
+    if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
         return std::nullopt;
     }
 
     // Tracks when we finish presenting to hwc
-    nsecs_t estimatedEndTime = mCommitStartTimes[0];
+    TimePoint estimatedHwcEndTime = mCommitStartTimes[0];
 
     // How long we spent this frame not doing anything, waiting for fences or vsync
-    nsecs_t idleDuration = 0;
+    Duration idleDuration = 0ns;
 
     // Most recent previous gpu end time in the current frame, probably from a prior display, used
     // as the start time for the next gpu operation if it ran over time since it probably blocked
-    std::optional<nsecs_t> previousValidGpuEndTime;
+    std::optional<TimePoint> previousValidGpuEndTime;
 
     // The currently estimated gpu end time for the frame,
     // used to accumulate gpu time as we iterate over the active displays
-    std::optional<nsecs_t> estimatedGpuEndTime;
-
-    // If we're predicting at the start of the frame, we use last frame as our reference point
-    // If we're predicting at the end of the frame, we use the current frame as a reference point
-    nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]);
-
-    // When the prior frame should be presenting to the display
-    // If we're predicting at the start of the frame, we use last frame's expected present time
-    // If we're predicting at the end of the frame, the present fence time is already known
-    nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime);
+    std::optional<TimePoint> estimatedGpuEndTime;
 
     // The timing info for the previously calculated display, if there was one
-    std::optional<DisplayTimeline> previousDisplayReferenceTiming;
+    std::optional<DisplayTimeline> previousDisplayTiming;
     std::vector<DisplayId>&& displayIds =
             getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
-    DisplayTimeline referenceTiming, estimatedTiming;
+    DisplayTimeline displayTiming;
 
     // Iterate over the displays that use hwc in the same order they are presented
     for (DisplayId displayId : displayIds) {
@@ -402,35 +428,26 @@
 
         auto& displayData = mDisplayTimingData.at(displayId);
 
-        // mLastPresentFenceTime should always be the time of the reference frame, since it will be
-        // the previous frame's present fence if called at the start, and current frame's if called
-        // at the end
-        referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
+        displayTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
 
         // If this is the first display, include the duration before hwc present starts
-        if (!previousDisplayReferenceTiming.has_value()) {
-            estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime;
+        if (!previousDisplayTiming.has_value()) {
+            estimatedHwcEndTime += displayTiming.hwcPresentStartTime - mCommitStartTimes[0];
         } else { // Otherwise add the time since last display's hwc present finished
-            estimatedEndTime += referenceTiming.hwcPresentStartTime -
-                    previousDisplayReferenceTiming->hwcPresentEndTime;
+            estimatedHwcEndTime +=
+                    displayTiming.hwcPresentStartTime - previousDisplayTiming->hwcPresentEndTime;
         }
 
-        // Late hint can re-use reference timing here since it's estimating its own reference frame
-        estimatedTiming = earlyHint
-                ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime,
-                                                                estimatedEndTime)
-                : referenceTiming;
-
         // Update predicted present finish time with this display's present time
-        estimatedEndTime = estimatedTiming.hwcPresentEndTime;
+        estimatedHwcEndTime = displayTiming.hwcPresentEndTime;
 
         // Track how long we spent waiting for the fence, can be excluded from the timing estimate
-        idleDuration += estimatedTiming.probablyWaitsForPresentFence
-                ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime
-                : 0;
+        idleDuration += displayTiming.probablyWaitsForPresentFence
+                ? mLastPresentFenceTime - displayTiming.presentFenceWaitStartTime
+                : 0ns;
 
         // Track how long we spent waiting to present, can be excluded from the timing estimate
-        idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration;
+        idleDuration += displayTiming.hwcPresentDelayDuration;
 
         // Estimate the reference frame's gpu timing
         auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
@@ -438,76 +455,54 @@
             previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
 
             // Estimate the prediction frame's gpu end time from the reference frame
-            estimatedGpuEndTime =
-                    std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) +
+            estimatedGpuEndTime = std::max(displayTiming.hwcPresentStartTime,
+                                           estimatedGpuEndTime.value_or(TimePoint{0ns})) +
                     gpuTiming->duration;
         }
-        previousDisplayReferenceTiming = referenceTiming;
+        previousDisplayTiming = displayTiming;
     }
-    ATRACE_INT64("Idle duration", idleDuration);
+    ATRACE_INT64("Idle duration", idleDuration.ns());
 
-    nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime;
+    TimePoint estimatedFlingerEndTime = mLastSfPresentEndTime;
 
     // Don't count time spent idly waiting in the estimate as we could do more work in that time
-    estimatedEndTime -= idleDuration;
+    estimatedHwcEndTime -= idleDuration;
     estimatedFlingerEndTime -= idleDuration;
 
     // We finish the frame when both present and the gpu are done, so wait for the later of the two
     // Also add the frame delay duration since the target did not move while we were delayed
-    nsecs_t totalDuration = mFrameDelayDuration +
-            std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0];
+    Duration totalDuration = mFrameDelayDuration +
+            std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
+            mCommitStartTimes[0];
 
     // We finish SurfaceFlinger when post-composition finishes, so add that in here
-    nsecs_t flingerDuration =
+    Duration flingerDuration =
             estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
 
     // Combine the two timings into a single normalized one
-    nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+    Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
 
     return std::make_optional(combinedDuration);
 }
 
-nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) {
-    nsecs_t targetDuration;
-    {
-        std::lock_guard lock(mPowerHalMutex);
-        targetDuration = *getPowerHal()->getTargetWorkDuration();
-    }
+Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) {
+    Duration targetDuration{0ns};
+    targetDuration = mTargetDuration;
     if (!mTotalFrameTargetDuration.has_value()) return flingerDuration;
 
     // Normalize total to the flinger target (vsync period) since that's how often we actually send
     // hints
-    nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration;
+    Duration normalizedTotalDuration = Duration::fromNs((targetDuration.ns() * totalDuration.ns()) /
+                                                        mTotalFrameTargetDuration->ns());
     return std::max(flingerDuration, normalizedTotalDuration);
 }
 
-PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference(
-        nsecs_t fenceTime, nsecs_t displayStartTime) {
-    DisplayTimeline estimated;
-    estimated.hwcPresentStartTime = displayStartTime;
-
-    // We don't predict waiting for vsync alignment yet
-    estimated.hwcPresentDelayDuration = 0;
-
-    // How long we expect to run before we start waiting for the fence
-    // For now just re-use last frame's post-present duration and assume it will not change much
-    // Excludes time spent waiting for vsync since that's not going to be consistent
-    estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime +
-            (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration));
-    estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime;
-    estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration +
-            (estimated.probablyWaitsForPresentFence ? fenceTime
-                                                    : estimated.presentFenceWaitStartTime);
-    return estimated;
-}
-
 PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline(
-        nsecs_t fenceTime) {
+        TimePoint fenceTime) {
     DisplayTimeline timeline;
     // How long between calling hwc present and trying to wait on the fence
-    const nsecs_t fenceWaitStartDelay =
-            (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated)
-                    .count();
+    const Duration fenceWaitStartDelay =
+            (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated);
 
     // Did our reference frame wait for an appropriate vsync before calling into hwc
     const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() &&
@@ -522,7 +517,7 @@
 
     // How long hwc present was delayed waiting for the next appropriate vsync
     timeline.hwcPresentDelayDuration =
-            (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0);
+            (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0ns);
     // When we started waiting for the present fence after calling into hwc present
     timeline.presentFenceWaitStartTime =
             timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay;
@@ -537,366 +532,45 @@
 }
 
 std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
-        std::optional<nsecs_t> previousEnd) {
+        std::optional<TimePoint> previousEndTime) {
     if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
         return std::nullopt;
     }
-    const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime);
-    const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime();
-    nsecs_t gpuDuration = 0;
-    if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID &&
-        latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) {
+    const TimePoint latestGpuStartTime =
+            std::max(previousEndTime.value_or(TimePoint{0ns}), *gpuStartTime);
+    const nsecs_t gpuEndFenceSignal = gpuEndFenceTime->getSignalTime();
+    Duration gpuDuration{0ns};
+    if (gpuEndFenceSignal != Fence::SIGNAL_TIME_INVALID &&
+        gpuEndFenceSignal != Fence::SIGNAL_TIME_PENDING) {
+        const TimePoint latestGpuEndTime = TimePoint::fromNs(gpuEndFenceSignal);
+
         // If we know how long the most recent gpu duration was, use that
         gpuDuration = latestGpuEndTime - latestGpuStartTime;
     } else if (lastValidGpuEndTime.has_value()) {
         // If we don't have the fence data, use the most recent information we do have
         gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime;
-        if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) {
+        if (gpuEndFenceSignal == Fence::SIGNAL_TIME_PENDING) {
             // If pending but went over the previous duration, use current time as the end
-            gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime);
+            gpuDuration = std::max(gpuDuration, Duration{TimePoint::now() - latestGpuStartTime});
         }
     }
     return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime};
 }
 
-class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
-public:
-    HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
-
-    ~HidlPowerHalWrapper() override = default;
-
-    static std::unique_ptr<HalWrapper> connect() {
-        // Power HAL 1.3 is not guaranteed to be available, thus we need to query
-        // Power HAL 1.0 first and try to cast it to Power HAL 1.3.
-        sp<V1_3::IPower> powerHal = nullptr;
-        sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
-        if (powerHal_1_0 != nullptr) {
-            // Try to cast to Power HAL 1.3
-            powerHal = V1_3::IPower::castFrom(powerHal_1_0);
-            if (powerHal == nullptr) {
-                ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor");
-            } else {
-                ALOGI("Loaded Power HAL 1.3 service");
-            }
-        } else {
-            ALOGW("No Power HAL found, disabling PowerAdvisor");
-        }
-
-        if (powerHal == nullptr) {
-            return nullptr;
-        }
-
-        return std::make_unique<HidlPowerHalWrapper>(std::move(powerHal));
-    }
-
-    bool setExpensiveRendering(bool enabled) override {
-        ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F");
-        auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled);
-        if (ret.isOk()) {
-            traceExpensiveRendering(enabled);
-        }
-        return ret.isOk();
-    }
-
-    bool notifyDisplayUpdateImminent() override {
-        // Power HAL 1.x doesn't have a notification for this
-        ALOGV("HIDL notifyUpdateImminent received but can't send");
-        return true;
-    }
-
-    bool supportsPowerHintSession() override { return false; }
-
-    bool isPowerHintSessionRunning() override { return false; }
-
-    void restartPowerHintSession() override {}
-
-    void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {}
-
-    bool startPowerHintSession() override { return false; }
-
-    void setTargetWorkDuration(int64_t) override {}
-
-    void sendActualWorkDuration(int64_t, nsecs_t) override {}
-
-    bool shouldReconnectHAL() override { return false; }
-
-    std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; }
-
-    std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; }
-
-private:
-    const sp<V1_3::IPower> mPowerHal = nullptr;
-};
-
-AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
-    auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
-    if (!ret.isOk()) {
-        mHasExpensiveRendering = false;
-    }
-
-    ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT, &mHasDisplayUpdateImminent);
-    if (!ret.isOk()) {
-        mHasDisplayUpdateImminent = false;
-    }
-
-    mSupportsPowerHint = checkPowerHintSessionSupported();
-
-    // Currently set to 0 to disable rate limiter by default
-    mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0);
-}
-
-AidlPowerHalWrapper::~AidlPowerHalWrapper() {
-    if (mPowerHintSession != nullptr) {
-        mPowerHintSession->close();
-        mPowerHintSession = nullptr;
-    }
-}
-
-std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() {
-    // This only waits if the service is actually declared
-    sp<IPower> powerHal = waitForVintfService<IPower>();
-    if (powerHal == nullptr) {
-        return nullptr;
-    }
-    ALOGI("Loaded AIDL Power HAL service");
-
-    return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
-}
-
-bool AidlPowerHalWrapper::setExpensiveRendering(bool enabled) {
-    ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
-    if (!mHasExpensiveRendering) {
-        ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
-        return true;
-    }
-
-    auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
-    if (ret.isOk()) {
-        traceExpensiveRendering(enabled);
-    }
-    return ret.isOk();
-}
-
-bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() {
-    ALOGV("AIDL notifyDisplayUpdateImminent");
-    if (!mHasDisplayUpdateImminent) {
-        ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
-        return true;
-    }
-
-    auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
-    return ret.isOk();
-}
-
-// Only version 2+ of the aidl supports power hint sessions, hidl has no support
-bool AidlPowerHalWrapper::supportsPowerHintSession() {
-    return mSupportsPowerHint;
-}
-
-bool AidlPowerHalWrapper::checkPowerHintSessionSupported() {
-    int64_t unused;
-    // Try to get preferred rate to determine if hint sessions are supported
-    // We check for isOk not EX_UNSUPPORTED_OPERATION to lump together errors
-    return mPowerHal->getHintSessionPreferredRate(&unused).isOk();
-}
-
-bool AidlPowerHalWrapper::isPowerHintSessionRunning() {
-    return mPowerHintSession != nullptr;
-}
-
-void AidlPowerHalWrapper::closePowerHintSession() {
-    if (mPowerHintSession != nullptr) {
-        mPowerHintSession->close();
-        mPowerHintSession = nullptr;
-    }
-}
-
-void AidlPowerHalWrapper::restartPowerHintSession() {
-    closePowerHintSession();
-    startPowerHintSession();
-}
-
-void AidlPowerHalWrapper::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
-    if (threadIds != mPowerHintThreadIds) {
-        mPowerHintThreadIds = threadIds;
-        if (isPowerHintSessionRunning()) {
-            restartPowerHintSession();
-        }
-    }
-}
-
-bool AidlPowerHalWrapper::startPowerHintSession() {
-    if (mPowerHintSession != nullptr || mPowerHintThreadIds.empty()) {
-        ALOGV("Cannot start power hint session, skipping");
-        return false;
-    }
-    auto ret =
-            mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
-                                         mPowerHintThreadIds, mTargetDuration, &mPowerHintSession);
-    if (!ret.isOk()) {
-        ALOGW("Failed to start power hint session with error: %s",
-              ret.exceptionToString(ret.exceptionCode()).c_str());
-    } else {
-        mLastTargetDurationSent = mTargetDuration;
-    }
-    return isPowerHintSessionRunning();
-}
-
-void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) {
-    ATRACE_CALL();
-    mTargetDuration = targetDuration;
-    if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration);
-    if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) {
-        ALOGV("Sending target time: %" PRId64 "ns", targetDuration);
-        mLastTargetDurationSent = targetDuration;
-        auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration);
-        if (!ret.isOk()) {
-            ALOGW("Failed to set power hint target work duration with error: %s",
-                  ret.exceptionMessage().c_str());
-            mShouldReconnectHal = true;
-        }
-    }
-}
-
-bool AidlPowerHalWrapper::shouldReportActualDurations() {
-    // Report if we have never reported before or are approaching a stale session
-    if (!mLastActualDurationSent.has_value() ||
-        (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) {
-        return true;
-    }
-
-    if (!mActualDuration.has_value()) {
-        return false;
-    }
-    // Report if the change in actual duration exceeds the threshold
-    return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation;
-}
-
-void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) {
-    ATRACE_CALL();
-
-    if (actualDuration < 0 || !isPowerHintSessionRunning()) {
-        ALOGV("Failed to send actual work duration, skipping");
-        return;
-    }
-    const nsecs_t reportedDuration = actualDuration;
-
-    mActualDuration = reportedDuration;
-    WorkDuration duration;
-    duration.durationNanos = reportedDuration;
-    duration.timeStampNanos = timestamp;
-    mPowerHintQueue.push_back(duration);
-
-    if (sTraceHintSessionData) {
-        ATRACE_INT64("Measured duration", actualDuration);
-        ATRACE_INT64("Target error term", actualDuration - mTargetDuration);
-
-        ATRACE_INT64("Reported duration", reportedDuration);
-        ATRACE_INT64("Reported target", mLastTargetDurationSent);
-        ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent);
-    }
-
-    ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
-          " with error: %" PRId64,
-          reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent);
-
-    // This rate limiter queues similar duration reports to the powerhal into
-    // batches to avoid excessive binder calls. The criteria to send a given batch
-    // are outlined in shouldReportActualDurationsNow()
-    if (shouldReportActualDurations()) {
-        ALOGV("Sending hint update batch");
-        mLastActualReportTimestamp = systemTime();
-        auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
-        if (!ret.isOk()) {
-            ALOGW("Failed to report actual work durations with error: %s",
-                  ret.exceptionMessage().c_str());
-            mShouldReconnectHal = true;
-        }
-        mPowerHintQueue.clear();
-        // We save the actual duration here for rate limiting
-        mLastActualDurationSent = actualDuration;
-    }
-}
-
-bool AidlPowerHalWrapper::shouldReconnectHAL() {
-    return mShouldReconnectHal;
-}
-
-std::vector<int32_t> AidlPowerHalWrapper::getPowerHintSessionThreadIds() {
-    return mPowerHintThreadIds;
-}
-
-std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() {
-    return mTargetDuration;
-}
-
-void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) {
-    mAllowedActualDeviation = allowedDeviation;
-}
-
-const bool AidlPowerHalWrapper::sTraceHintSessionData =
+const bool PowerAdvisor::sTraceHintSessionData =
         base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);
 
-PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
-    if (!mHasHal) {
-        return nullptr;
-    }
+const Duration PowerAdvisor::sTargetSafetyMargin = std::chrono::microseconds(
+        base::GetIntProperty<int64_t>("debug.sf.hint_margin_us",
+                                      ticks<std::micro>(PowerAdvisor::kDefaultTargetSafetyMargin)));
 
-    // Grab old hint session values before we destroy any existing wrapper
-    std::vector<int32_t> oldPowerHintSessionThreadIds;
-    std::optional<int64_t> oldTargetWorkDuration;
+const bool PowerAdvisor::sUseReportActualDuration =
+        base::GetBoolProperty(std::string("debug.adpf.use_report_actual_duration"), true);
 
-    if (mHalWrapper != nullptr) {
-        oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds();
-        oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration();
-    }
-
-    // If we used to have a HAL, but it stopped responding, attempt to reconnect
-    if (mReconnectPowerHal) {
-        mHalWrapper = nullptr;
-        mReconnectPowerHal = false;
-    }
-
-    if (mHalWrapper != nullptr) {
-        auto wrapper = mHalWrapper.get();
-        // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
-        if (!wrapper->shouldReconnectHAL()) {
-            return wrapper;
-        }
-        ALOGD("Reconnecting Power HAL");
-        mHalWrapper = nullptr;
-    }
-
-    // At this point, we know for sure there is no running session
-    mPowerHintSessionRunning = false;
-
-    // First attempt to connect to the AIDL Power HAL
-    mHalWrapper = AidlPowerHalWrapper::connect();
-
-    // If that didn't succeed, attempt to connect to the HIDL Power HAL
-    if (mHalWrapper == nullptr) {
-        mHalWrapper = HidlPowerHalWrapper::connect();
-    } else {
-        ALOGD("Successfully connecting AIDL Power HAL");
-        // If AIDL, pass on any existing hint session values
-        mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
-        // Only set duration and start if duration is defined
-        if (oldTargetWorkDuration.has_value()) {
-            mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
-            // Only start if possible to run and both threadids and duration are defined
-            if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
-                mPowerHintSessionRunning = mHalWrapper->startPowerHintSession();
-            }
-        }
-    }
-
-    // If we make it to this point and still don't have a HAL, it's unlikely we
-    // will, so stop trying
-    if (mHalWrapper == nullptr) {
-        mHasHal = false;
-    }
-
-    return mHalWrapper.get();
+power::PowerHalController& PowerAdvisor::getPowerHal() {
+    static std::once_flag halFlag;
+    std::call_once(halFlag, [this] { mPowerHal->init(); });
+    return *mPowerHal;
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 6e25f78..f0d3fd8 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -27,6 +27,8 @@
 
 #include <android/hardware/power/IPower.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <powermanager/PowerHalController.h>
+#include <scheduler/Time.h>
 #include <ui/DisplayIdentification.h>
 #include "../Scheduler/OneShotTimer.h"
 
@@ -47,51 +49,50 @@
     virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
     virtual bool isUsingExpensiveRendering() = 0;
-    virtual void notifyDisplayUpdateImminent() = 0;
+    virtual void notifyDisplayUpdateImminentAndCpuReset() = 0;
     // Checks both if it supports and if it's enabled
     virtual bool usePowerHintSession() = 0;
     virtual bool supportsPowerHintSession() = 0;
-    virtual bool isPowerHintSessionRunning() = 0;
+
+    virtual bool ensurePowerHintSessionRunning() = 0;
     // Sends a power hint that updates to the target work duration for the frame
-    virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
+    virtual void updateTargetWorkDuration(Duration targetDuration) = 0;
     // Sends a power hint for the actual known work duration at the end of the frame
-    virtual void sendActualWorkDuration() = 0;
-    // Sends a power hint for the upcoming frame predicted from previous frame timing
-    virtual void sendPredictedWorkDuration() = 0;
+    virtual void reportActualWorkDuration() = 0;
     // Sets whether the power hint session is enabled
-    virtual void enablePowerHint(bool enabled) = 0;
+    virtual void enablePowerHintSession(bool enabled) = 0;
     // Initializes the power hint session
     virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0;
     // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
     virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
     // Reports the start and end times of a hwc validate call this frame for a given display
-    virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime,
-                                      nsecs_t validateEndTime) = 0;
+    virtual void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                                      TimePoint validateEndTime) = 0;
     // Reports the start and end times of a hwc present call this frame for a given display
-    virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                                     nsecs_t presentEndTime) = 0;
+    virtual void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                                     TimePoint presentEndTime) = 0;
     // Reports the expected time that the current frame will present to the display
-    virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0;
+    virtual void setExpectedPresentTime(TimePoint expectedPresentTime) = 0;
     // Reports the most recent present fence time and end time once known
-    virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0;
+    virtual void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) = 0;
     // Reports whether a display used client composition this frame
     virtual void setRequiresClientComposition(DisplayId displayId,
                                               bool requiresClientComposition) = 0;
     // Reports whether a given display skipped validation this frame
     virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
     // Reports when a hwc present is delayed, and the time that it will resume
-    virtual void setHwcPresentDelayedTime(
-            DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0;
+    virtual void setHwcPresentDelayedTime(DisplayId displayId,
+                                          TimePoint earliestFrameStartTime) = 0;
     // Reports the start delay for SurfaceFlinger this frame
-    virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0;
+    virtual void setFrameDelay(Duration frameDelayDuration) = 0;
     // Reports the SurfaceFlinger commit start time this frame
-    virtual void setCommitStart(nsecs_t commitStartTime) = 0;
+    virtual void setCommitStart(TimePoint commitStartTime) = 0;
     // Reports the SurfaceFlinger composite end time this frame
-    virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0;
+    virtual void setCompositeEnd(TimePoint compositeEndTime) = 0;
     // Reports the list of the currently active displays
     virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0;
     // Sets the target duration for the entire pipeline including the gpu
-    virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0;
+    virtual void setTotalFrameTargetWorkDuration(Duration targetDuration) = 0;
 };
 
 namespace impl {
@@ -100,24 +101,6 @@
 // full state of the system when sending out power hints to things like the GPU.
 class PowerAdvisor final : public Hwc2::PowerAdvisor {
 public:
-    class HalWrapper {
-    public:
-        virtual ~HalWrapper() = default;
-
-        virtual bool setExpensiveRendering(bool enabled) = 0;
-        virtual bool notifyDisplayUpdateImminent() = 0;
-        virtual bool supportsPowerHintSession() = 0;
-        virtual bool isPowerHintSessionRunning() = 0;
-        virtual void restartPowerHintSession() = 0;
-        virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
-        virtual bool startPowerHintSession() = 0;
-        virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0;
-        virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0;
-        virtual bool shouldReconnectHAL() = 0;
-        virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
-        virtual std::optional<nsecs_t> getTargetWorkDuration() = 0;
-    };
-
     PowerAdvisor(SurfaceFlinger& flinger);
     ~PowerAdvisor() override;
 
@@ -125,46 +108,35 @@
     void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
     bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
-    void notifyDisplayUpdateImminent() override;
+    void notifyDisplayUpdateImminentAndCpuReset() override;
     bool usePowerHintSession() override;
     bool supportsPowerHintSession() override;
-    bool isPowerHintSessionRunning() override;
-    void setTargetWorkDuration(nsecs_t targetDuration) override;
-    void sendActualWorkDuration() override;
-    void sendPredictedWorkDuration() override;
-    void enablePowerHint(bool enabled) override;
+    bool ensurePowerHintSessionRunning() override;
+    void updateTargetWorkDuration(Duration targetDuration) override;
+    void reportActualWorkDuration() override;
+    void enablePowerHintSession(bool enabled) override;
     bool startPowerHintSession(const std::vector<int32_t>& threadIds) override;
     void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime);
-    void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime,
-                              nsecs_t validateEndTime) override;
-    void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime,
-                             nsecs_t presentEndTime) override;
+    void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
+                              TimePoint validateEndTime) override;
+    void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
+                             TimePoint presentEndTime) override;
     void setSkippedValidate(DisplayId displayId, bool skipped) override;
     void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
-    void setExpectedPresentTime(nsecs_t expectedPresentTime) override;
-    void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override;
-    void setHwcPresentDelayedTime(
-            DisplayId displayId,
-            std::chrono::steady_clock::time_point earliestFrameStartTime) override;
+    void setExpectedPresentTime(TimePoint expectedPresentTime) override;
+    void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
+    void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;
 
-    void setFrameDelay(nsecs_t frameDelayDuration) override;
-    void setCommitStart(nsecs_t commitStartTime) override;
-    void setCompositeEnd(nsecs_t compositeEndTime) override;
+    void setFrameDelay(Duration frameDelayDuration) override;
+    void setCommitStart(TimePoint commitStartTime) override;
+    void setCompositeEnd(TimePoint compositeEndTime) override;
     void setDisplays(std::vector<DisplayId>& displayIds) override;
-    void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;
+    void setTotalFrameTargetWorkDuration(Duration targetDuration) override;
 
 private:
     friend class PowerAdvisorTest;
 
-    // Tracks if powerhal exists
-    bool mHasHal = true;
-    // Holds the hal wrapper for getPowerHal
-    std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;
-
-    HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
-    bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
-    std::mutex mPowerHalMutex;
-
+    std::unique_ptr<power::PowerHalController> mPowerHal;
     std::atomic_bool mBootFinished = false;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
@@ -178,44 +150,42 @@
     // Higher-level timing data used for estimation
     struct DisplayTimeline {
         // The start of hwc present, or the start of validate if it happened there instead
-        nsecs_t hwcPresentStartTime = -1;
+        TimePoint hwcPresentStartTime;
         // The end of hwc present or validate, whichever one actually presented
-        nsecs_t hwcPresentEndTime = -1;
+        TimePoint hwcPresentEndTime;
         // How long the actual hwc present was delayed after hwcPresentStartTime
-        nsecs_t hwcPresentDelayDuration = 0;
+        Duration hwcPresentDelayDuration{0ns};
         // When we think we started waiting for the present fence after calling into hwc present and
         // after potentially waiting for the earliest present time
-        nsecs_t presentFenceWaitStartTime = -1;
+        TimePoint presentFenceWaitStartTime;
         // How long we ran after we finished waiting for the fence but before hwc present finished
-        nsecs_t postPresentFenceHwcPresentDuration = 0;
+        Duration postPresentFenceHwcPresentDuration{0ns};
         // Are we likely to have waited for the present fence during composition
         bool probablyWaitsForPresentFence = false;
-        // Estimate one frame's timeline from that of a previous frame
-        DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime);
     };
 
     struct GpuTimeline {
-        nsecs_t duration = 0;
-        nsecs_t startTime = -1;
+        Duration duration{0ns};
+        TimePoint startTime;
     };
 
     // Power hint session data recorded from the pipeline
     struct DisplayTimingData {
         std::unique_ptr<FenceTime> gpuEndFenceTime;
-        std::optional<nsecs_t> gpuStartTime;
-        std::optional<nsecs_t> lastValidGpuEndTime;
-        std::optional<nsecs_t> lastValidGpuStartTime;
-        std::optional<nsecs_t> hwcPresentStartTime;
-        std::optional<nsecs_t> hwcPresentEndTime;
-        std::optional<nsecs_t> hwcValidateStartTime;
-        std::optional<nsecs_t> hwcValidateEndTime;
-        std::optional<nsecs_t> hwcPresentDelayedTime;
+        std::optional<TimePoint> gpuStartTime;
+        std::optional<TimePoint> lastValidGpuEndTime;
+        std::optional<TimePoint> lastValidGpuStartTime;
+        std::optional<TimePoint> hwcPresentStartTime;
+        std::optional<TimePoint> hwcPresentEndTime;
+        std::optional<TimePoint> hwcValidateStartTime;
+        std::optional<TimePoint> hwcValidateEndTime;
+        std::optional<TimePoint> hwcPresentDelayedTime;
         bool usedClientComposition = false;
         bool skippedValidate = false;
         // Calculate high-level timing milestones from more granular display timing data
-        DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime);
+        DisplayTimeline calculateDisplayTimeline(TimePoint fenceTime);
         // Estimate the gpu duration for a given display from previous gpu timing data
-        std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd);
+        std::optional<GpuTimeline> estimateGpuTiming(std::optional<TimePoint> previousEndTime);
     };
 
     template <class T, size_t N>
@@ -240,107 +210,71 @@
     };
 
     // Filter and sort the display ids by a given property
-    std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy);
+    std::vector<DisplayId> getOrderedDisplayIds(
+            std::optional<TimePoint> DisplayTimingData::*sortBy);
     // Estimates a frame's total work duration including gpu time.
-    // Runs either at the beginning or end of a frame, using the most recent data available
-    std::optional<nsecs_t> estimateWorkDuration(bool earlyHint);
+    std::optional<Duration> estimateWorkDuration();
     // There are two different targets and actual work durations we care about,
     // this normalizes them together and takes the max of the two
-    nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration);
+    Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
 
     std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
 
     // Current frame's delay
-    nsecs_t mFrameDelayDuration = 0;
+    Duration mFrameDelayDuration{0ns};
     // Last frame's post-composition duration
-    nsecs_t mLastPostcompDuration = 0;
+    Duration mLastPostcompDuration{0ns};
     // Buffer of recent commit start times
-    RingBuffer<nsecs_t, 2> mCommitStartTimes;
+    RingBuffer<TimePoint, 2> mCommitStartTimes;
     // Buffer of recent expected present times
-    RingBuffer<nsecs_t, 2> mExpectedPresentTimes;
-    // Most recent present fence time, set at the end of the frame once known
-    nsecs_t mLastPresentFenceTime = -1;
-    // Most recent present fence time, set at the end of the frame once known
-    nsecs_t mLastSfPresentEndTime = -1;
-    // Target for the entire pipeline including gpu
-    std::optional<nsecs_t> mTotalFrameTargetDuration;
+    RingBuffer<TimePoint, 2> mExpectedPresentTimes;
+    // Most recent present fence time, provided by SF after composition engine finishes presenting
+    TimePoint mLastPresentFenceTime;
+    // Most recent composition engine present end time, returned with the present fence from SF
+    TimePoint mLastSfPresentEndTime;
+    // Target duration for the entire pipeline including gpu
+    std::optional<Duration> mTotalFrameTargetDuration;
     // Updated list of display IDs
     std::vector<DisplayId> mDisplayIds;
 
-    std::optional<bool> mPowerHintEnabled;
-    std::optional<bool> mSupportsPowerHint;
-    bool mPowerHintSessionRunning = false;
+    // Ensure powerhal connection is initialized
+    power::PowerHalController& getPowerHal();
+
+    std::optional<bool> mHintSessionEnabled;
+    std::optional<bool> mSupportsHintSession;
+    bool mHintSessionRunning = false;
+
+    std::mutex mHintSessionMutex;
+    sp<hardware::power::IPowerHintSession> mHintSession GUARDED_BY(mHintSessionMutex) = nullptr;
+
+    // Initialize to true so we try to call, to check if it's supported
+    bool mHasExpensiveRendering = true;
+    bool mHasDisplayUpdateImminent = true;
+    // Queue of actual durations saved to report
+    std::vector<hardware::power::WorkDuration> mHintSessionQueue;
+    // The latest values we have received for target and actual
+    Duration mTargetDuration = kDefaultTargetDuration;
+    std::optional<Duration> mActualDuration;
+    // The list of thread ids, stored so we can restart the session from this class if needed
+    std::vector<int32_t> mHintSessionThreadIds;
+    Duration mLastTargetDurationSent = kDefaultTargetDuration;
+    // Whether we should emit ATRACE_INT data for hint sessions
+    static const bool sTraceHintSessionData;
+
+    // Default target duration for the hint session
+    static constexpr const Duration kDefaultTargetDuration{16ms};
 
     // An adjustable safety margin which pads the "actual" value sent to PowerHAL,
     // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error
-    static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms;
+    static const Duration sTargetSafetyMargin;
+    static constexpr const Duration kDefaultTargetSafetyMargin{1ms};
+
+    // Whether we should send reportActualWorkDuration calls
+    static const bool sUseReportActualDuration;
 
     // How long we expect hwc to run after the present call until it waits for the fence
-    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us;
-    static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us;
-};
-
-class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
-public:
-    explicit AidlPowerHalWrapper(sp<hardware::power::IPower> powerHal);
-    ~AidlPowerHalWrapper() override;
-
-    static std::unique_ptr<HalWrapper> connect();
-
-    bool setExpensiveRendering(bool enabled) override;
-    bool notifyDisplayUpdateImminent() override;
-    bool supportsPowerHintSession() override;
-    bool isPowerHintSessionRunning() override;
-    void restartPowerHintSession() override;
-    void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
-    bool startPowerHintSession() override;
-    void setTargetWorkDuration(nsecs_t targetDuration) override;
-    void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override;
-    bool shouldReconnectHAL() override;
-    std::vector<int32_t> getPowerHintSessionThreadIds() override;
-    std::optional<nsecs_t> getTargetWorkDuration() override;
-
-private:
-    friend class AidlPowerHalWrapperTest;
-
-    bool checkPowerHintSessionSupported();
-    void closePowerHintSession();
-    bool shouldReportActualDurations();
-
-    // Used for testing
-    void setAllowedActualDeviation(nsecs_t);
-
-    const sp<hardware::power::IPower> mPowerHal = nullptr;
-    bool mHasExpensiveRendering = false;
-    bool mHasDisplayUpdateImminent = false;
-    // Used to indicate an error state and need for reconstruction
-    bool mShouldReconnectHal = false;
-
-    // Power hint session data
-
-    // Concurrent access for this is protected by mPowerHalMutex
-    sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr;
-    // Queue of actual durations saved to report
-    std::vector<hardware::power::WorkDuration> mPowerHintQueue;
-    // The latest values we have received for target and actual
-    nsecs_t mTargetDuration = kDefaultTarget.count();
-    std::optional<nsecs_t> mActualDuration;
-    // The list of thread ids, stored so we can restart the session from this class if needed
-    std::vector<int32_t> mPowerHintThreadIds;
-    bool mSupportsPowerHint = false;
-    // Keep track of the last messages sent for rate limiter change detection
-    std::optional<nsecs_t> mLastActualDurationSent;
-    // Timestamp of the last report we sent, used to avoid stale sessions
-    nsecs_t mLastActualReportTimestamp = 0;
-    nsecs_t mLastTargetDurationSent = kDefaultTarget.count();
-    // Max amount the error term can vary without causing an actual value report
-    nsecs_t mAllowedActualDeviation = -1;
-    // Whether we should emit ATRACE_INT data for hint sessions
-    static const bool sTraceHintSessionData;
-    static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms;
-    // Amount of time after the last message was sent before the session goes stale
-    // actually 100ms but we use 80 here to ideally avoid going stale
-    static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms;
+    static constexpr const Duration kFenceWaitStartDelayValidated{150us};
+    static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..d62075e 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -103,6 +103,10 @@
     sink->setAsyncMode(true);
     IGraphicBufferProducer::QueueBufferOutput output;
     mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
+
+    for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+        mHwcBufferIds[i] = UINT64_MAX;
+    }
 }
 
 VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -197,9 +201,9 @@
         return NO_MEMORY;
     }
 
-    sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
-            mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
-    sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+    sp<GraphicBuffer> const& fbBuffer =
+            mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
+    sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot];
     VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
              mOutputProducerSlot, outBuffer.get());
 
@@ -211,12 +215,14 @@
 
     status_t result = NO_ERROR;
     if (fbBuffer != nullptr) {
-        uint32_t hwcSlot = 0;
-        sp<GraphicBuffer> hwcBuffer;
-        mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
-
+        // assume that HWC has previously seen the buffer in this slot
+        sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+        if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) {
+            mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId();
+            hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
+        }
         // TODO: Correctly propagate the dataspace from GL composition
-        result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
+        result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
                                       ui::Dataspace::UNKNOWN);
     }
 
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..be06e2b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,7 +20,7 @@
 #include <string>
 
 #include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
 #include <gui/ConsumerBase.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <ui/DisplayId.h>
@@ -164,6 +164,10 @@
     sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
     uint32_t mDefaultOutputFormat;
 
+    // Buffers that HWC has seen before, indexed by HWC slot number.
+    // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+    uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
+
     //
     // Inter-frame state
     //
@@ -260,8 +264,6 @@
 
     bool mMustRecompose = false;
 
-    compositionengine::impl::HwcBufferCache mHwcBufferCache;
-
     bool mForceHwcCopy;
 };
 
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index 20486e0..e55cd3e 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -35,20 +35,23 @@
                                                       const Rect& sourceCrop, ui::Size reqSize,
                                                       ui::Dataspace reqDataSpace,
                                                       bool useIdentityTransform,
+                                                      bool hintForSeamlessTransition,
                                                       bool allowSecureLayers) {
     if (auto display = displayWeak.promote()) {
         // Using new to access a private constructor.
         return std::unique_ptr<DisplayRenderArea>(
                 new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
-                                      useIdentityTransform, allowSecureLayers));
+                                      useIdentityTransform, hintForSeamlessTransition,
+                                      allowSecureLayers));
     }
     return nullptr;
 }
 
 DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
                                      ui::Size reqSize, ui::Dataspace reqDataSpace,
-                                     bool useIdentityTransform, bool allowSecureLayers)
-      : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(),
+                                     bool useIdentityTransform, bool hintForSeamlessTransition,
+                                     bool allowSecureLayers)
+      : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
                    allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)),
         mDisplay(std::move(display)),
         mSourceCrop(sourceCrop) {}
@@ -57,18 +60,6 @@
     return mTransform;
 }
 
-Rect DisplayRenderArea::getBounds() const {
-    return mDisplay->getBounds();
-}
-
-int DisplayRenderArea::getHeight() const {
-    return mDisplay->getHeight();
-}
-
-int DisplayRenderArea::getWidth() const {
-    return mDisplay->getWidth();
-}
-
 bool DisplayRenderArea::isSecure() const {
     return mAllowSecureLayers && mDisplay->isSecure();
 }
@@ -77,18 +68,6 @@
     return mDisplay;
 }
 
-bool DisplayRenderArea::needsFiltering() const {
-    // check if the projection from the logical render area
-    // to the physical render area requires filtering
-    const Rect& sourceCrop = getSourceCrop();
-    int width = sourceCrop.width();
-    int height = sourceCrop.height();
-    if (getRotationFlags() & ui::Transform::ROT_90) {
-        std::swap(width, height);
-    }
-    return width != getReqWidth() || height != getReqHeight();
-}
-
 Rect DisplayRenderArea::getSourceCrop() const {
     // use the projected display viewport by default.
     if (mSourceCrop.isEmpty()) {
@@ -107,4 +86,4 @@
     return rotation.transform(mSourceCrop);
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index 3478fc1..9a4981c 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -30,24 +30,22 @@
     static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
                                               ui::Size reqSize, ui::Dataspace,
                                               bool useIdentityTransform,
+                                              bool hintForSeamlessTransition,
                                               bool allowSecureLayers = true);
 
     const ui::Transform& getTransform() const override;
-    Rect getBounds() const override;
-    int getHeight() const override;
-    int getWidth() const override;
     bool isSecure() const override;
     sp<const DisplayDevice> getDisplayDevice() const override;
-    bool needsFiltering() const override;
     Rect getSourceCrop() const override;
 
 private:
     DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
-                      ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true);
+                      ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition,
+                      bool allowSecureLayers = true);
 
     const sp<const DisplayDevice> mDisplay;
     const Rect mSourceCrop;
     const ui::Transform mTransform;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
deleted file mode 100644
index e8c590e..0000000
--- a/services/surfaceflinger/EffectLayer.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2007 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"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "EffectLayer"
-
-#include "EffectLayer.h"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-EffectLayer::EffectLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {}
-
-EffectLayer::~EffectLayer() = default;
-
-std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClientCompositionList(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    std::vector<compositionengine::LayerFE::LayerSettings> results;
-    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
-            prepareClientComposition(targetSettings);
-    // Nothing to render.
-    if (!layerSettings) {
-        return {};
-    }
-
-    // set the shadow for the layer if needed
-    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
-
-    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
-    if (targetSettings.realContentIsVisible && fillsColor()) {
-        // Set color for color fill settings.
-        layerSettings->source.solidColor = getColor().rgb;
-        results.push_back(*layerSettings);
-    } else if (hasBlur() || drawShadows()) {
-        layerSettings->skipContentDraw = true;
-        results.push_back(*layerSettings);
-    }
-
-    return results;
-}
-
-bool EffectLayer::isVisible() const {
-    return !isHiddenByPolicy() && (getAlpha() > 0.0_hf || hasBlur()) && hasSomethingToDraw();
-}
-
-bool EffectLayer::setColor(const half3& color) {
-    if (mDrawingState.color.r == color.r && mDrawingState.color.g == color.g &&
-        mDrawingState.color.b == color.b) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.color.r = color.r;
-    mDrawingState.color.g = color.g;
-    mDrawingState.color.b = color.b;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool EffectLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mDrawingState.dataspace == dataspace) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.dataspace = dataspace;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-void EffectLayer::preparePerFrameCompositionState() {
-    Layer::preparePerFrameCompositionState();
-
-    auto* compositionState = editCompositionState();
-    compositionState->color = getColor();
-    compositionState->compositionType =
-            aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
-}
-
-sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
-    // There's no need to get a CE Layer if the EffectLayer isn't going to draw anything. In that
-    // case, it acts more like a ContainerLayer so returning a null CE Layer makes more sense
-    if (hasSomethingToDraw()) {
-        return asLayerFE();
-    } else {
-        return nullptr;
-    }
-}
-
-compositionengine::LayerFECompositionState* EffectLayer::editCompositionState() {
-    return mCompositionState.get();
-}
-
-const compositionengine::LayerFECompositionState* EffectLayer::getCompositionState() const {
-    return mCompositionState.get();
-}
-
-bool EffectLayer::isOpaque(const Layer::State& s) const {
-    // Consider the layer to be opaque if its opaque flag is set or its effective
-    // alpha (considering the alpha of its parents as well) is 1.0;
-    return (s.flags & layer_state_t::eLayerOpaque) != 0 || (fillsColor() && getAlpha() == 1.0_hf);
-}
-
-ui::Dataspace EffectLayer::getDataSpace() const {
-    return mDrawingState.dataspace;
-}
-
-sp<Layer> EffectLayer::createClone() {
-    sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
-            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
-    layer->setInitialValuesForClone(this);
-    return layer;
-}
-
-bool EffectLayer::fillsColor() const {
-    return mDrawingState.color.r >= 0.0_hf && mDrawingState.color.g >= 0.0_hf &&
-            mDrawingState.color.b >= 0.0_hf;
-}
-
-bool EffectLayer::hasBlur() const {
-    return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
deleted file mode 100644
index 1dcb633..0000000
--- a/services/surfaceflinger/EffectLayer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2007 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 <sys/types.h>
-
-#include <cstdint>
-
-#include "Layer.h"
-
-namespace android {
-
-// A layer that can render a combination of the following effects.
-//   * fill the bounds of the layer with a color
-//   * render a shadow cast by the bounds of the layer
-// If no effects are enabled, the layer is considered to be invisible.
-class EffectLayer : public Layer {
-public:
-    explicit EffectLayer(const LayerCreationArgs&);
-    ~EffectLayer() override;
-
-    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
-    compositionengine::LayerFECompositionState* editCompositionState() override;
-
-    const char* getType() const override { return "EffectLayer"; }
-    bool isVisible() const override;
-
-    bool setColor(const half3& color) override;
-
-    bool setDataspace(ui::Dataspace dataspace) override;
-
-    ui::Dataspace getDataSpace() const override;
-
-    bool isOpaque(const Layer::State& s) const override;
-
-protected:
-    /*
-     * compositionengine::LayerFE overrides
-     */
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    void preparePerFrameCompositionState() override;
-    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
-            compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) override;
-
-    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
-
-    sp<Layer> createClone() override;
-
-private:
-    // Returns true if there is a valid color to fill.
-    bool fillsColor() const;
-    // Returns true if this layer has a blur value.
-    bool hasBlur() const;
-    bool hasSomethingToDraw() const { return fillsColor() || drawShadows() || hasBlur(); }
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
index e12835f..155cf4d 100644
--- a/services/surfaceflinger/FpsReporter.cpp
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -56,14 +56,15 @@
 
     mFlinger.mCurrentState.traverse([&](Layer* layer) {
         auto& currentState = layer->getDrawingState();
-        if (currentState.metadata.has(METADATA_TASK_ID)) {
-            int32_t taskId = currentState.metadata.getInt32(METADATA_TASK_ID, 0);
+        if (currentState.metadata.has(gui::METADATA_TASK_ID)) {
+            int32_t taskId = currentState.metadata.getInt32(gui::METADATA_TASK_ID, 0);
             if (seenTasks.count(taskId) == 0) {
                 // localListeners is expected to be tiny
                 for (TrackedListener& listener : localListeners) {
                     if (listener.taskId == taskId) {
                         seenTasks.insert(taskId);
-                        listenersAndLayersToReport.push_back({listener, sp<Layer>(layer)});
+                        listenersAndLayersToReport.push_back(
+                                {listener, sp<Layer>::fromExisting(layer)});
                         break;
                     }
                 }
@@ -90,7 +91,7 @@
 
 void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, int32_t taskId) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
-    asBinder->linkToDeath(this);
+    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
     std::lock_guard lock(mMutex);
     mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, taskId});
 }
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 66beff2..ded734e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -109,11 +109,11 @@
         jankType &= ~JankType::DisplayHAL;
     }
     if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
-        janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+        janks.emplace_back("SurfaceFlinger deadline missed (while in HWC)");
         jankType &= ~JankType::SurfaceFlingerCpuDeadlineMissed;
     }
     if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
-        janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+        janks.emplace_back("SurfaceFlinger deadline missed (while in GPU comp)");
         jankType &= ~JankType::SurfaceFlingerGpuDeadlineMissed;
     }
     if (jankType & JankType::AppDeadlineMissed) {
@@ -289,7 +289,7 @@
         minTime = std::min(minTime, actuals.endTime);
     }
     if (actuals.presentTime != 0) {
-        minTime = std::min(minTime, actuals.endTime);
+        minTime = std::min(minTime, actuals.presentTime);
     }
     return minTime;
 }
@@ -885,13 +885,19 @@
 
 void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
                                                nsecs_t previousPresentTime) {
-    if (mPredictionState == PredictionState::Expired ||
-        mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
+    const bool presentTimeValid =
+            mSurfaceFlingerActuals.presentTime >= mSurfaceFlingerActuals.startTime;
+    if (mPredictionState == PredictionState::Expired || !presentTimeValid) {
         // Cannot do jank classification with expired predictions or invalid signal times. Set the
         // deltas to 0 as both negative and positive deltas are used as real values.
         mJankType = JankType::Unknown;
         deadlineDelta = 0;
         deltaToVsync = 0;
+        if (!presentTimeValid) {
+            mSurfaceFlingerActuals.presentTime = mSurfaceFlingerActuals.endTime;
+            mJankType |= JankType::DisplayHAL;
+        }
+
         return;
     }
 
@@ -986,11 +992,8 @@
                                     mJankClassificationThresholds.presentThreshold) {
                     // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame
                     // was presented more than a vsync late.
-                    if (mGpuFence != FenceTime::NO_FENCE &&
-                        mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime <
-                                mRefreshRate.getPeriodNsecs()) {
-                        // If SF was in GPU composition and the CPU work finished before the vsync
-                        // period, classify it as GPU deadline missed.
+                    if (mGpuFence != FenceTime::NO_FENCE) {
+                        // If SF was in GPU composition, classify it as GPU deadline missed.
                         mJankType = JankType::SurfaceFlingerGpuDeadlineMissed;
                     } else {
                         mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
@@ -1094,6 +1097,12 @@
 }
 
 void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+    if (mSurfaceFrames.empty()) {
+        // We don't want to trace display frames without any surface frames updates as this cannot
+        // be janky
+        return;
+    }
+
     if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
         // DisplayFrame should not have an invalid token.
         ALOGE("Cannot trace DisplayFrame with invalid token");
@@ -1171,22 +1180,50 @@
             static_cast<float>(totalPresentToPresentWalls);
 }
 
+std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const {
+    for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+        const auto& [fence, _] = mPendingPresentFences[i];
+        if (fence && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+            return i;
+        }
+    }
+
+    return {};
+}
+
 void FrameTimeline::flushPendingPresentFences() {
+    const auto firstSignaledFence = getFirstSignalFenceIndex();
+    if (!firstSignaledFence.has_value()) {
+        return;
+    }
+
     // Perfetto is using boottime clock to void drifts when the device goes
     // to suspend.
     const auto monoBootOffset = mUseBootTimeClock
             ? (systemTime(SYSTEM_TIME_BOOTTIME) - systemTime(SYSTEM_TIME_MONOTONIC))
             : 0;
 
+    // Present fences are expected to be signaled in order. Mark all the previous
+    // pending fences as errors.
+    for (size_t i = 0; i < firstSignaledFence.value(); i++) {
+        const auto& pendingPresentFence = *mPendingPresentFences.begin();
+        const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+        auto& displayFrame = pendingPresentFence.second;
+        displayFrame->onPresent(signalTime, mPreviousPresentTime);
+        displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
+        mPendingPresentFences.erase(mPendingPresentFences.begin());
+    }
+
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
         nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
         if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
             signalTime = pendingPresentFence.first->getSignalTime();
             if (signalTime == Fence::SIGNAL_TIME_PENDING) {
-                continue;
+                break;
             }
         }
+
         auto& displayFrame = pendingPresentFence.second;
         displayFrame->onPresent(signalTime, mPreviousPresentTime);
         displayFrame->trace(mSurfaceFlingerPid, monoBootOffset);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 3611dea..d54d22d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -474,6 +474,7 @@
     friend class android::frametimeline::FrameTimelineTest;
 
     void flushPendingPresentFences() REQUIRES(mMutex);
+    std::optional<size_t> getFirstSignalFenceIndex() const REQUIRES(mMutex);
     void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
     void dumpAll(std::string& result);
     void dumpJank(std::string& result);
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
new file mode 100644
index 0000000..218a64a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <sstream>
+
+#include <gui/DisplayInfo.h>
+
+namespace android::surfaceflinger::frontend {
+
+// Display information needed to populate input and calculate layer geometry.
+struct DisplayInfo {
+    gui::DisplayInfo info;
+    ui::Transform transform;
+    bool receivesInput;
+    bool isSecure;
+    // TODO(b/259407931): can eliminate once SurfaceFlinger::sActiveDisplayRotationFlags is removed.
+    bool isPrimary;
+    bool isVirtual;
+    ui::Transform::RotationFlags rotationFlags;
+    ui::Transform::RotationFlags transformHint;
+    std::string getDebugString() const {
+        std::stringstream debug;
+        debug << "DisplayInfo {displayId=" << info.displayId << " lw=" << info.logicalWidth
+              << " lh=" << info.logicalHeight << " transform={" << transform.dsdx() << " ,"
+              << transform.dsdy() << " ," << transform.dtdx() << " ," << transform.dtdy()
+              << "} isSecure=" << isSecure << " isPrimary=" << isPrimary
+              << " rotationFlags=" << rotationFlags << " transformHint=" << transformHint << "}";
+        return debug.str();
+    }
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
new file mode 100644
index 0000000..97af445
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "LayerCreationArgs.h"
+#include <binder/IPCThreadState.h>
+#include <private/android_filesystem_config.h>
+#include "Client.h"
+#include "gui/LayerMetadata.h"
+
+namespace android::surfaceflinger {
+
+std::atomic<uint32_t> LayerCreationArgs::sSequence{1};
+std::atomic<uint32_t> LayerCreationArgs::sInternalSequence{1};
+
+uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) {
+    return id | INTERNAL_LAYER_PREFIX;
+}
+
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+                                     uint32_t flags, gui::LayerMetadata metadataArg,
+                                     std::optional<uint32_t> id, bool internalLayer)
+      : flinger(flinger),
+        client(std::move(client)),
+        name(std::move(name)),
+        flags(flags),
+        metadata(std::move(metadataArg)) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    ownerPid = ipc->getCallingPid();
+    uid_t callingUid = ipc->getCallingUid();
+    metadata.setInt32(gui::METADATA_CALLING_UID, static_cast<int32_t>(callingUid));
+    ownerUid = callingUid;
+    if (ownerUid == AID_GRAPHICS || ownerUid == AID_SYSTEM) {
+        // System can override the calling UID/PID since it can create layers on behalf of apps.
+        ownerPid = metadata.getInt32(gui::METADATA_OWNER_PID, ownerPid);
+        ownerUid = static_cast<uid_t>(
+                metadata.getInt32(gui::METADATA_OWNER_UID, static_cast<int32_t>(ownerUid)));
+    }
+
+    if (internalLayer) {
+        sequence = id.value_or(getInternalLayerId(sInternalSequence++));
+    } else if (id) {
+        sequence = *id;
+        sSequence = *id + 1;
+    } else {
+        sequence = sSequence++;
+        if (sequence >= INTERNAL_LAYER_PREFIX) {
+            sSequence = 1;
+            ALOGW("Layer sequence id rolled over.");
+            sequence = sSequence++;
+        }
+    }
+}
+
+LayerCreationArgs::LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer)
+      : LayerCreationArgs(nullptr, nullptr, /*name=*/"", /*flags=*/0, /*metadata=*/{}, id,
+                          internalLayer) {}
+
+LayerCreationArgs LayerCreationArgs::fromOtherArgs(const LayerCreationArgs& other) {
+    // returns a new instance of LayerCreationArgs with a unique id.
+    return LayerCreationArgs(other.flinger, other.client, other.name, other.flags, other.metadata);
+}
+
+std::string LayerCreationArgs::getDebugString() const {
+    std::stringstream stream;
+    stream << "LayerCreationArgs{" << name << "[" << sequence << "] flags=" << flags
+           << " pid=" << ownerPid << " uid=" << ownerUid;
+    if (addToRoot) {
+        stream << " addToRoot=" << addToRoot;
+    }
+    if (parentId != UNASSIGNED_LAYER_ID) {
+        stream << " parentId=" << parentId;
+    }
+    if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
+        stream << " layerIdToMirror=" << layerIdToMirror;
+    }
+    if (layerStackToMirror != ui::INVALID_LAYER_STACK) {
+        stream << " layerStackToMirror=" << layerStackToMirror.id;
+    }
+    return stream.str();
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
new file mode 100644
index 0000000..c26edb5
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -0,0 +1,67 @@
+/*
+ * 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/Binder.h>
+#include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
+#include <utils/StrongPointer.h>
+#include <cstdint>
+#include <limits>
+#include <optional>
+
+constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
+constexpr uint32_t INTERNAL_LAYER_PREFIX = 1u << 31;
+
+namespace android {
+class SurfaceFlinger;
+class Client;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+struct LayerCreationArgs {
+    static std::atomic<uint32_t> sSequence;
+    static std::atomic<uint32_t> sInternalSequence;
+    static uint32_t getInternalLayerId(uint32_t id);
+    static LayerCreationArgs fromOtherArgs(const LayerCreationArgs& other);
+
+    LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name,
+                      uint32_t flags, gui::LayerMetadata, std::optional<uint32_t> id = std::nullopt,
+                      bool internalLayer = false);
+    LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer = false);
+    LayerCreationArgs() = default; // for tracing
+    std::string getDebugString() const;
+
+    android::SurfaceFlinger* flinger;
+    sp<android::Client> client;
+    std::string name;
+    uint32_t flags; // ISurfaceComposerClient flags
+    gui::LayerMetadata metadata;
+    pid_t ownerPid;
+    uid_t ownerUid;
+    uint32_t textureName;
+    uint32_t sequence;
+    bool addToRoot = true;
+    wp<IBinder> parentHandle = nullptr;
+    wp<IBinder> mirrorLayerHandle = nullptr;
+    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
new file mode 100644
index 0000000..75e4e3a
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 "LayerHandle.h"
+#include <cstdint>
+#include "Layer.h"
+#include "LayerCreationArgs.h"
+#include "SurfaceFlinger.h"
+
+namespace android::surfaceflinger {
+
+LayerHandle::LayerHandle(const sp<android::SurfaceFlinger>& flinger,
+                         const sp<android::Layer>& layer)
+      : mFlinger(flinger), mLayer(layer), mLayerId(static_cast<uint32_t>(layer->getSequence())) {}
+
+LayerHandle::~LayerHandle() {
+    if (mFlinger) {
+        mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+    }
+}
+
+const String16 LayerHandle::kDescriptor = String16("android.Layer.LayerHandle");
+
+sp<LayerHandle> LayerHandle::fromIBinder(const sp<IBinder>& binder) {
+    if (binder == nullptr) {
+        return nullptr;
+    }
+
+    BBinder* b = binder->localBinder();
+    if (b == nullptr || b->getInterfaceDescriptor() != LayerHandle::kDescriptor) {
+        ALOGD("handle does not have a valid descriptor");
+        return nullptr;
+    }
+
+    // We can safely cast this binder since its local and we verified its interface descriptor.
+    return sp<LayerHandle>::cast(binder);
+}
+
+sp<android::Layer> LayerHandle::getLayer(const sp<IBinder>& binder) {
+    sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+    return handle ? handle->mLayer : nullptr;
+}
+
+uint32_t LayerHandle::getLayerId(const sp<IBinder>& binder) {
+    sp<LayerHandle> handle = LayerHandle::fromIBinder(binder);
+    return handle ? handle->mLayerId : UNASSIGNED_LAYER_ID;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.h b/services/surfaceflinger/FrontEnd/LayerHandle.h
new file mode 100644
index 0000000..5d0f783
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.h
@@ -0,0 +1,58 @@
+/*
+ * 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/Binder.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+class SurfaceFlinger;
+class Layer;
+} // namespace android
+
+namespace android::surfaceflinger {
+
+/*
+ * The layer handle is just a BBinder object passed to the client
+ * (remote process) -- we don't keep any reference on our side such that
+ * the dtor is called when the remote side let go of its reference.
+ *
+ * ~LayerHandle ensures that mFlinger->onLayerDestroyed() is called for
+ * this layer when the handle is destroyed.
+ */
+class LayerHandle : public BBinder {
+public:
+    LayerHandle(const sp<android::SurfaceFlinger>& flinger, const sp<android::Layer>& layer);
+    // for testing
+    LayerHandle(uint32_t layerId) : mFlinger(nullptr), mLayer(nullptr), mLayerId(layerId) {}
+    ~LayerHandle();
+
+    // Static functions to access the layer and layer id safely from an incoming binder.
+    static sp<LayerHandle> fromIBinder(const sp<IBinder>& handle);
+    static sp<android::Layer> getLayer(const sp<IBinder>& handle);
+    static uint32_t getLayerId(const sp<IBinder>& handle);
+    static const String16 kDescriptor;
+
+    const String16& getInterfaceDescriptor() const override { return kDescriptor; }
+
+private:
+    sp<android::SurfaceFlinger> mFlinger;
+    sp<android::Layer> mLayer;
+    const uint32_t mLayerId;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..5913d4b
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,487 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "LayerLog.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+                        const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+    auto lhsLayer = lhs.first->getLayer();
+    auto rhsLayer = rhs.first->getLayer();
+    if (lhsLayer->layerStack.id != rhsLayer->layerStack.id) {
+        return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+    }
+    if (lhsLayer->z != rhsLayer->z) {
+        return lhsLayer->z < rhsLayer->z;
+    }
+    return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+                  std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+    auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+    vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+    mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+    mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+                              LayerHierarchy::TraversalPath& traversalPath) const {
+    if (mLayer) {
+        bool breakTraversal = !visitor(*this, traversalPath);
+        if (breakTraversal) {
+            return;
+        }
+    }
+    if (traversalPath.hasRelZLoop()) {
+        LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+    }
+    for (auto& [child, childVariant] : mChildren) {
+        ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+                                                         childVariant);
+        child->traverse(visitor, traversalPath);
+    }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+                                      LayerHierarchy::TraversalPath& traversalPath) const {
+    bool traverseThisLayer = (mLayer != nullptr);
+    for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+        auto& [child, childVariant] = *it;
+        if (traverseThisLayer && child->getLayer()->z >= 0) {
+            traverseThisLayer = false;
+            bool breakTraversal = !visitor(*this, traversalPath);
+            if (breakTraversal) {
+                return;
+            }
+        }
+        if (childVariant == LayerHierarchy::Variant::Detached) {
+            continue;
+        }
+        ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+                                                         childVariant);
+        child->traverseInZOrder(visitor, traversalPath);
+    }
+
+    if (traverseThisLayer) {
+        visitor(*this, traversalPath);
+    }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+    insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+    auto it = std::find_if(mChildren.begin(), mChildren.end(),
+                           [child](const std::pair<LayerHierarchy*, Variant>& x) {
+                               return x.first == child;
+                           });
+    if (it == mChildren.end()) {
+        LOG_ALWAYS_FATAL("Could not find child!");
+    }
+    mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+    std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+    auto it = std::find_if(mChildren.begin(), mChildren.end(),
+                           [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+                               return child.first == hierarchy;
+                           });
+    if (it == mChildren.end()) {
+        LOG_ALWAYS_FATAL("Could not find child!");
+    } else {
+        it->second = variant;
+    }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+    return mLayer;
+}
+
+const LayerHierarchy* LayerHierarchy::getRelativeParent() const {
+    return mRelativeParent;
+}
+
+const LayerHierarchy* LayerHierarchy::getParent() const {
+    return mParent;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+    std::string debug = "LayerHierarchy{";
+    debug += ((mLayer) ? mLayer->getDebugString() : "root") + " ";
+    if (mChildren.empty()) {
+        debug += "no children";
+    } else {
+        debug += std::to_string(mChildren.size()) + " children";
+    }
+    return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+    std::string debug = prefix + getDebugStringShort();
+    for (auto& [child, childVariant] : mChildren) {
+        std::string childPrefix = "  " + std::string(prefix) + " " + std::to_string(childVariant);
+        debug += "\n" + child->getDebugString(childPrefix.c_str());
+    }
+    return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+    outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+    traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+                                       const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+        if (traversalPath.hasRelZLoop()) {
+            outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+            return false;
+        }
+        return true;
+    });
+    return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+        const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+    mHierarchies.reserve(layers.size());
+    mLayerIdToHierarchy.reserve(layers.size());
+    for (auto& layer : layers) {
+        mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+        mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+    }
+    for (const auto& layer : layers) {
+        onLayerAdded(layer.get());
+    }
+    detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+    auto layer = hierarchy->mLayer;
+    LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+            ? LayerHierarchy::Variant::Detached
+            : LayerHierarchy::Variant::Attached;
+
+    LayerHierarchy* parent;
+
+    if (layer->parentId != UNASSIGNED_LAYER_ID) {
+        parent = getHierarchyFromId(layer->parentId);
+    } else if (layer->canBeRoot) {
+        parent = &mRoot;
+    } else {
+        parent = &mOffscreenRoot;
+    }
+    parent->addChild(hierarchy, type);
+    hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+    hierarchy->mParent->removeChild(hierarchy);
+    hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+    auto layer = hierarchy->mLayer;
+    if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+        return;
+    }
+
+    if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+        hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+    } else {
+        hierarchy->mRelativeParent = &mOffscreenRoot;
+    }
+    hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+    hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+    if (hierarchy->mRelativeParent) {
+        hierarchy->mRelativeParent->removeChild(hierarchy);
+    }
+    hierarchy->mRelativeParent = nullptr;
+    hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+    if (root->mLayer) {
+        attachToRelativeParent(root);
+    }
+    for (auto& [child, childVariant] : root->mChildren) {
+        if (childVariant == LayerHierarchy::Variant::Detached ||
+            childVariant == LayerHierarchy::Variant::Attached) {
+            attachHierarchyToRelativeParent(child);
+        }
+    }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+    if (root->mLayer) {
+        detachFromRelativeParent(root);
+    }
+    for (auto& [child, childVariant] : root->mChildren) {
+        if (childVariant == LayerHierarchy::Variant::Detached ||
+            childVariant == LayerHierarchy::Variant::Attached) {
+            detachHierarchyFromRelativeParent(child);
+        }
+    }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+    attachToParent(hierarchy);
+    attachToRelativeParent(hierarchy);
+
+    for (uint32_t mirrorId : layer->mirrorIds) {
+        LayerHierarchy* mirror = getHierarchyFromId(mirrorId);
+        hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+    }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+    LLOGV(layer->id, "");
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+    if (!hierarchy) {
+        // Layer was never part of the hierarchy if it was created and destroyed in the same
+        // transaction.
+        return;
+    }
+    // detach from parent
+    detachFromRelativeParent(hierarchy);
+    detachFromParent(hierarchy);
+
+    // detach children
+    for (auto& [child, variant] : hierarchy->mChildren) {
+        if (variant == LayerHierarchy::Variant::Attached ||
+            variant == LayerHierarchy::Variant::Detached) {
+            mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+            child->mParent = &mOffscreenRoot;
+        } else if (variant == LayerHierarchy::Variant::Relative) {
+            mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+            child->mRelativeParent = &mOffscreenRoot;
+        }
+    }
+
+    swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+        return layerHierarchy.get() == hierarchy;
+    });
+    mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+    LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+    auto it = hierarchy->mChildren.begin();
+    while (it != hierarchy->mChildren.end()) {
+        if (it->second == LayerHierarchy::Variant::Mirror) {
+            it = hierarchy->mChildren.erase(it);
+        } else {
+            it++;
+        }
+    }
+
+    for (uint32_t mirrorId : layer->mirrorIds) {
+        hierarchy->addChild(getHierarchyFromId(mirrorId), LayerHierarchy::Variant::Mirror);
+    }
+}
+
+void LayerHierarchyBuilder::update(
+        const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+        const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+    // rebuild map
+    for (auto& layer : layers) {
+        if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+            mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+            mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+        }
+    }
+
+    for (auto& layer : layers) {
+        if (layer->changes.get() == 0) {
+            continue;
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+            onLayerAdded(layer.get());
+            continue;
+        }
+        LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+        if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+            detachFromParent(hierarchy);
+            attachToParent(hierarchy);
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+            detachFromRelativeParent(hierarchy);
+            attachToRelativeParent(hierarchy);
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+            hierarchy->mParent->sortChildrenByZOrder();
+            if (hierarchy->mRelativeParent) {
+                hierarchy->mRelativeParent->sortChildrenByZOrder();
+            }
+        }
+        if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+            updateMirrorLayer(layer.get());
+        }
+    }
+
+    for (auto& layer : destroyedLayers) {
+        onLayerDestroyed(layer.get());
+    }
+    // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+    // from our relative parents. This walks down both trees to do so. We can optimize this
+    // further by tracking onscreen, offscreen state in LayerHierarchy.
+    detachHierarchyFromRelativeParent(&mOffscreenRoot);
+    attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+    return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+    return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+    if (depth > 10) return "too deep, loop?";
+    if (layerId == UNASSIGNED_LAYER_ID) return "";
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) return "not found";
+
+    LayerHierarchy* hierarchy = it->second;
+    if (!hierarchy->mLayer) return "none";
+
+    std::string debug =
+            "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+    if (hierarchy->mRelativeParent) {
+        debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+    }
+    if (hierarchy->mParent) {
+        debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+    }
+    return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+                                                          bool childrenOnly) const {
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+    LayerHierarchy hierarchy(*it->second, childrenOnly);
+    return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+    auto it = mLayerIdToHierarchy.find(layerId);
+    if (it == mLayerIdToHierarchy.end()) {
+        if (crashOnFailure) {
+            LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+        }
+        return nullptr;
+    };
+
+    return it->second;
+}
+
+const LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT =
+        {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+    if (id == UNASSIGNED_LAYER_ID) {
+        return "TraversalPath{ROOT}";
+    }
+    std::stringstream ss;
+    ss << "TraversalPath{.id = " << id;
+
+    if (mirrorRootId != UNASSIGNED_LAYER_ID) {
+        ss << ", .mirrorRootId=" << mirrorRootId;
+    }
+
+    if (!relativeRootIds.empty()) {
+        ss << ", .relativeRootIds=";
+        for (auto rootId : relativeRootIds) {
+            ss << rootId << ",";
+        }
+    }
+
+    if (hasRelZLoop()) {
+        ss << "hasRelZLoop=true invalidRelativeRootId=" << invalidRelativeRootId << ",";
+    }
+    ss << "}";
+    return ss.str();
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::getMirrorRoot() const {
+    LOG_ALWAYS_FATAL_IF(!isClone(), "Cannot get mirror root of a non cloned node");
+    TraversalPath mirrorRootPath = *this;
+    mirrorRootPath.id = mirrorRootId;
+    return mirrorRootPath;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+                                                                   uint32_t layerId,
+                                                                   LayerHierarchy::Variant variant)
+      : mTraversalPath(traversalPath), mParentPath(traversalPath) {
+    // Update the traversal id with the child layer id and variant. Parent id and variant are
+    // stored to reset the id upon destruction.
+    traversalPath.id = layerId;
+    traversalPath.variant = variant;
+    if (variant == LayerHierarchy::Variant::Mirror) {
+        traversalPath.mirrorRootId = mParentPath.id;
+    } else if (variant == LayerHierarchy::Variant::Relative) {
+        if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+                      layerId) != traversalPath.relativeRootIds.end()) {
+            traversalPath.invalidRelativeRootId = layerId;
+        }
+        traversalPath.relativeRootIds.emplace_back(layerId);
+    } else if (variant == LayerHierarchy::Variant::Detached) {
+        traversalPath.detached = true;
+    }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+    // Reset the traversal id to its original parent state using the state that was saved in
+    // the constructor.
+    if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+        mTraversalPath.mirrorRootId = mParentPath.mirrorRootId;
+    } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+        mTraversalPath.relativeRootIds.pop_back();
+    }
+    if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+        mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+    }
+    mTraversalPath.id = mParentPath.id;
+    mTraversalPath.variant = mParentPath.variant;
+    mTraversalPath.detached = mParentPath.detached;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..b25b731
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,212 @@
+/*
+ * 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 "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+    enum Variant : uint32_t {
+        Attached,
+        Detached,
+        Relative,
+        Mirror,
+        ftl_first = Attached,
+        ftl_last = Mirror,
+    };
+    // Represents a unique path to a node.
+    // The layer hierarchy is represented as a graph. Each node can be visited by multiple parents.
+    // This allows us to represent mirroring in an efficient way. See the example below:
+    // root
+    // ├─ A {Traversal path id = 1}
+    // ├─ B {Traversal path id = 2}
+    // │  ├─ C {Traversal path id = 3}
+    // │  ├─ D {Traversal path id = 4}
+    // │  └─ E {Traversal path id = 5}
+    // ├─ F (Mirrors B) {Traversal path id = 6}
+    // └─ G (Mirrors F) {Traversal path id = 7}
+    //
+    // C, D and E can be traversed via B or via F then B or via G then F then B.
+    // Depending on how the node is reached, its properties such as geometry or visibility might be
+    // different. And we can uniquely identify the node by keeping track of the nodes leading up to
+    // it. But to be more efficient we only need to track the nodes id and the top mirror root path.
+    // So C for example, would have the following unique traversal paths:
+    //  - {Traversal path id = 3}
+    //  - {Traversal path id = 3, mirrorRootId = 6}
+    //  - {Traversal path id = 3, mirrorRootId = 7}
+
+    struct TraversalPath {
+        uint32_t id;
+        LayerHierarchy::Variant variant;
+        // Mirrored layers can have a different geometry than their parents so we need to track
+        // the mirror roots in the traversal.
+        uint32_t mirrorRootId = UNASSIGNED_LAYER_ID;
+        // Relative layers can be visited twice, once by their parent and then once again by
+        // their relative parent. We keep track of the roots here to detect any loops in the
+        // hierarchy. If a relative root already exists in the list while building the
+        // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+        // parented to each other.
+        ftl::SmallVector<uint32_t, 5> relativeRootIds;
+        // First duplicate relative root id found. If this is a valid layer id that means we are
+        // in a loop.
+        uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+        // See isAttached()
+        bool detached = false;
+        bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+        // Returns true if this node is reached via one or more relative parents.
+        bool isRelative() const { return !relativeRootIds.empty(); }
+        // Returns true if the node or its parents are not Detached.
+        bool isAttached() const { return !detached; }
+        // Returns true if the node is a clone.
+        bool isClone() const { return mirrorRootId != UNASSIGNED_LAYER_ID; }
+        TraversalPath getMirrorRoot() const;
+
+        bool operator==(const TraversalPath& other) const {
+            return id == other.id && mirrorRootId == other.mirrorRootId;
+        }
+        std::string toString() const;
+
+        static const TraversalPath ROOT;
+    };
+
+    struct TraversalPathHash {
+        std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
+            uint32_t hashCode = key.id * 31;
+            if (key.mirrorRootId != UNASSIGNED_LAYER_ID) {
+                hashCode += key.mirrorRootId * 31;
+            }
+            return std::hash<size_t>{}(hashCode);
+        }
+    };
+
+    // Helper class to add nodes to an existing traversal id and removes the
+    // node when it goes out of scope.
+    class ScopedAddToTraversalPath {
+    public:
+        ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+                                 LayerHierarchy::Variant variantArg);
+        ~ScopedAddToTraversalPath();
+
+    private:
+        TraversalPath& mTraversalPath;
+        TraversalPath mParentPath;
+    };
+    LayerHierarchy(RequestedLayerState* layer);
+
+    // Visitor function that provides the hierarchy node and a traversal id which uniquely
+    // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+    // Return false to stop traversing down the hierarchy.
+    typedef std::function<bool(const LayerHierarchy& hierarchy,
+                               const LayerHierarchy::TraversalPath& traversalPath)>
+            Visitor;
+
+    // Traverse the hierarchy and visit all child variants.
+    void traverse(const Visitor& visitor) const {
+        TraversalPath root = TraversalPath::ROOT;
+        if (mLayer) {
+            root.id = mLayer->id;
+        }
+        traverse(visitor, root);
+    }
+
+    // Traverse the hierarchy in z-order, skipping children that have relative parents.
+    void traverseInZOrder(const Visitor& visitor) const {
+        TraversalPath root = TraversalPath::ROOT;
+        if (mLayer) {
+            root.id = mLayer->id;
+        }
+        traverseInZOrder(visitor, root);
+    }
+
+    const RequestedLayerState* getLayer() const;
+    const LayerHierarchy* getRelativeParent() const;
+    const LayerHierarchy* getParent() const;
+    std::string getDebugString(const char* prefix = "") const;
+    std::string getDebugStringShort() const;
+    // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+    // will contain the first relative root that was visited twice in a traversal.
+    bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+    std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+    friend LayerHierarchyBuilder;
+    LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+    void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+    void removeChild(LayerHierarchy*);
+    void sortChildrenByZOrder();
+    void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+    void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+    void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+    const RequestedLayerState* mLayer;
+    LayerHierarchy* mParent = nullptr;
+    LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// hierarchy from a list of RequestedLayerState and associated change flags.
+class LayerHierarchyBuilder {
+public:
+    LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+    void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+                const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+    LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+    const LayerHierarchy& getHierarchy() const;
+    const LayerHierarchy& getOffscreenHierarchy() const;
+    std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+    void onLayerAdded(RequestedLayerState* layer);
+    void attachToParent(LayerHierarchy*);
+    void detachFromParent(LayerHierarchy*);
+    void attachToRelativeParent(LayerHierarchy*);
+    void detachFromRelativeParent(LayerHierarchy*);
+    void attachHierarchyToRelativeParent(LayerHierarchy*);
+    void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+    void onLayerDestroyed(RequestedLayerState* layer);
+    void updateMirrorLayer(RequestedLayerState* layer);
+    LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+    std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
+    std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
+    LayerHierarchy mRoot{nullptr};
+    LayerHierarchy mOffscreenRoot{nullptr};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
new file mode 100644
index 0000000..cd9515c
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#undef LOG_TAG
+#define LOG_TAG "LayerLifecycleManager"
+
+#include "LayerLifecycleManager.h"
+#include "Client.h" // temporarily needed for LayerCreationArgs
+#include "LayerLog.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+using namespace ftl::flag_operators;
+
+void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayerState>> newLayers) {
+    if (newLayers.empty()) {
+        return;
+    }
+
+    mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+    for (auto& newLayer : newLayers) {
+        RequestedLayerState& layer = *newLayer.get();
+        auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
+        if (!inserted) {
+            LOG_ALWAYS_FATAL("Duplicate layer id found. New layer: %s Existing layer: %s",
+                             layer.getDebugString().c_str(),
+                             it->second.owner.getDebugString().c_str());
+        }
+        mAddedLayers.push_back(newLayer.get());
+        layer.parentId = linkLayer(layer.parentId, layer.id);
+        layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+        if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+            // if this layer is mirroring a display, then walk though all the existing root layers
+            // for the layer stack and add them as children to be mirrored.
+            mDisplayMirroringLayers.emplace_back(layer.id);
+            for (auto& rootLayer : mLayers) {
+                if (rootLayer->isRoot() && rootLayer->layerStack == layer.layerStackToMirror) {
+                    layer.mirrorIds.emplace_back(rootLayer->id);
+                    linkLayer(rootLayer->id, layer.id);
+                }
+            }
+        } else {
+            // Check if we are mirroring a single layer, and if so add it to the list of children
+            // to be mirrored.
+            layer.layerIdToMirror = linkLayer(layer.layerIdToMirror, layer.id);
+            if (layer.layerIdToMirror != UNASSIGNED_LAYER_ID) {
+                layer.mirrorIds.emplace_back(layer.layerIdToMirror);
+            }
+        }
+        layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
+        if (layer.isRoot()) {
+            updateDisplayMirrorLayers(layer);
+        }
+        LLOGV(layer.id, "%s", layer.getDebugString().c_str());
+        mLayers.emplace_back(std::move(newLayer));
+    }
+}
+
+void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles,
+                                               bool ignoreUnknownHandles) {
+    std::vector<uint32_t> layersToBeDestroyed;
+    for (const auto& layerId : destroyedHandles) {
+        auto it = mIdToLayer.find(layerId);
+        if (it == mIdToLayer.end()) {
+            LOG_ALWAYS_FATAL_IF(!ignoreUnknownHandles, "%s Layerid not found %d", __func__,
+                                layerId);
+            continue;
+        }
+        RequestedLayerState& layer = it->second.owner;
+        LLOGV(layer.id, "%s", layer.getDebugString().c_str());
+        layer.handleAlive = false;
+        if (!layer.canBeDestroyed()) {
+            continue;
+        }
+        layer.changes |= RequestedLayerState::Changes::Destroyed;
+        layersToBeDestroyed.emplace_back(layerId);
+    }
+
+    if (layersToBeDestroyed.empty()) {
+        return;
+    }
+
+    mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+    for (size_t i = 0; i < layersToBeDestroyed.size(); i++) {
+        uint32_t layerId = layersToBeDestroyed[i];
+        auto it = mIdToLayer.find(layerId);
+        if (it == mIdToLayer.end()) {
+            LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId);
+            continue;
+        }
+
+        RequestedLayerState& layer = it->second.owner;
+
+        layer.parentId = unlinkLayer(layer.parentId, layer.id);
+        layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+        if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+            layer.mirrorIds = unlinkLayers(layer.mirrorIds, layer.id);
+            swapErase(mDisplayMirroringLayers, layer.id);
+        } else {
+            layer.layerIdToMirror = unlinkLayer(layer.layerIdToMirror, layer.id);
+            layer.mirrorIds.clear();
+        }
+
+        layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
+
+        auto& references = it->second.references;
+        for (uint32_t linkedLayerId : references) {
+            RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId);
+            if (!linkedLayer) {
+                LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__,
+                                 linkedLayerId, layer.id);
+                continue;
+            };
+            if (linkedLayer->parentId == layer.id) {
+                linkedLayer->parentId = UNASSIGNED_LAYER_ID;
+                if (linkedLayer->canBeDestroyed()) {
+                    linkedLayer->changes |= RequestedLayerState::Changes::Destroyed;
+                    layersToBeDestroyed.emplace_back(linkedLayer->id);
+                }
+            }
+            if (linkedLayer->relativeParentId == layer.id) {
+                linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID;
+            }
+            if (swapErase(linkedLayer->mirrorIds, layer.id)) {
+                linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
+            }
+            if (linkedLayer->touchCropId == layer.id) {
+                linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
+            }
+        }
+        mIdToLayer.erase(it);
+    }
+
+    auto it = mLayers.begin();
+    while (it != mLayers.end()) {
+        RequestedLayerState* layer = it->get();
+        if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) {
+            LLOGV(layer->id, "destroyed %s", layer->getDebugStringShort().c_str());
+            std::iter_swap(it, mLayers.end() - 1);
+            mDestroyedLayers.emplace_back(std::move(mLayers.back()));
+            if (it == mLayers.end() - 1) {
+                it = mLayers.erase(mLayers.end() - 1);
+            } else {
+                mLayers.erase(mLayers.end() - 1);
+            }
+        } else {
+            it++;
+        }
+    }
+}
+
+void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState>& transactions,
+                                              bool ignoreUnknownLayers) {
+    for (const auto& transaction : transactions) {
+        for (const auto& resolvedComposerState : transaction.states) {
+            const auto& clientState = resolvedComposerState.state;
+            uint32_t layerId = resolvedComposerState.layerId;
+            if (layerId == UNASSIGNED_LAYER_ID) {
+                ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get());
+                continue;
+            }
+
+            RequestedLayerState* layer = getLayerFromId(layerId);
+            if (layer == nullptr) {
+                LOG_ALWAYS_FATAL_IF(!ignoreUnknownLayers, "%s Layer with layerid=%d not found",
+                                    __func__, layerId);
+                continue;
+            }
+
+            if (!layer->handleAlive) {
+                LOG_ALWAYS_FATAL("%s Layer's with layerid=%d) is not alive. Possible out of "
+                                 "order LayerLifecycleManager updates",
+                                 __func__, layerId);
+                continue;
+            }
+
+            if (transaction.flags & ISurfaceComposer::eAnimation) {
+                layer->changes |= RequestedLayerState::Changes::Animation;
+            }
+
+            uint32_t oldParentId = layer->parentId;
+            uint32_t oldRelativeParentId = layer->relativeParentId;
+            uint32_t oldTouchCropId = layer->touchCropId;
+            layer->merge(resolvedComposerState);
+
+            if (layer->what & layer_state_t::eBackgroundColorChanged) {
+                if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) {
+                    LayerCreationArgs
+                            backgroundLayerArgs(LayerCreationArgs::getInternalLayerId(
+                                                        LayerCreationArgs::sInternalSequence++),
+                                                /*internalLayer=*/true);
+                    backgroundLayerArgs.parentId = layer->id;
+                    backgroundLayerArgs.name = layer->name + "BackgroundColorLayer";
+                    backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect;
+                    std::vector<std::unique_ptr<RequestedLayerState>> newLayers;
+                    newLayers.emplace_back(
+                            std::make_unique<RequestedLayerState>(backgroundLayerArgs));
+                    RequestedLayerState* backgroundLayer = newLayers.back().get();
+                    backgroundLayer->bgColorLayer = true;
+                    backgroundLayer->handleAlive = false;
+                    backgroundLayer->parentId = layer->id;
+                    backgroundLayer->z = std::numeric_limits<int32_t>::min();
+                    backgroundLayer->color = layer->bgColor;
+                    backgroundLayer->dataspace = layer->bgColorDataspace;
+                    layer->bgColorLayerId = backgroundLayer->id;
+                    addLayers({std::move(newLayers)});
+                } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && layer->bgColor.a == 0) {
+                    RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+                    layer->bgColorLayerId = UNASSIGNED_LAYER_ID;
+                    bgColorLayer->parentId = unlinkLayer(bgColorLayer->parentId, bgColorLayer->id);
+                    onHandlesDestroyed({bgColorLayer->id});
+                } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
+                    RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
+                    bgColorLayer->color = layer->bgColor;
+                    bgColorLayer->dataspace = layer->bgColorDataspace;
+                    bgColorLayer->what |= layer_state_t::eColorChanged |
+                            layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged;
+                    bgColorLayer->changes |= RequestedLayerState::Changes::Content;
+                    mGlobalChanges |= RequestedLayerState::Changes::Content;
+                }
+            }
+
+            if (oldParentId != layer->parentId) {
+                unlinkLayer(oldParentId, layer->id);
+                layer->parentId = linkLayer(layer->parentId, layer->id);
+                if (oldParentId == UNASSIGNED_LAYER_ID) {
+                    updateDisplayMirrorLayers(*layer);
+                }
+            }
+            if (layer->what & layer_state_t::eLayerStackChanged && layer->isRoot()) {
+                updateDisplayMirrorLayers(*layer);
+            }
+            if (oldRelativeParentId != layer->relativeParentId) {
+                unlinkLayer(oldRelativeParentId, layer->id);
+                layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
+            }
+            if (oldTouchCropId != layer->touchCropId) {
+                unlinkLayer(oldTouchCropId, layer->id);
+                layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
+            }
+
+            mGlobalChanges |= layer->changes;
+        }
+    }
+}
+
+void LayerLifecycleManager::commitChanges() {
+    for (auto layer : mAddedLayers) {
+        for (auto& listener : mListeners) {
+            listener->onLayerAdded(*layer);
+        }
+    }
+    mAddedLayers.clear();
+
+    for (auto& layer : mLayers) {
+        layer->clearChanges();
+    }
+
+    for (auto& destroyedLayer : mDestroyedLayers) {
+        for (auto& listener : mListeners) {
+            listener->onLayerDestroyed(*destroyedLayer);
+        }
+    }
+    mDestroyedLayers.clear();
+    mGlobalChanges.clear();
+}
+
+void LayerLifecycleManager::addLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+    mListeners.emplace_back(std::move(listener));
+}
+
+void LayerLifecycleManager::removeLifecycleListener(std::shared_ptr<ILifecycleListener> listener) {
+    swapErase(mListeners, listener);
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getLayers() const {
+    return mLayers;
+}
+
+const std::vector<std::unique_ptr<RequestedLayerState>>& LayerLifecycleManager::getDestroyedLayers()
+        const {
+    return mDestroyedLayers;
+}
+
+const ftl::Flags<RequestedLayerState::Changes> LayerLifecycleManager::getGlobalChanges() const {
+    return mGlobalChanges;
+}
+
+RequestedLayerState* LayerLifecycleManager::getLayerFromId(uint32_t id) {
+    if (id == UNASSIGNED_LAYER_ID) {
+        return nullptr;
+    }
+    auto it = mIdToLayer.find(id);
+    if (it == mIdToLayer.end()) {
+        return nullptr;
+    }
+    return &it->second.owner;
+}
+
+std::vector<uint32_t>* LayerLifecycleManager::getLinkedLayersFromId(uint32_t id) {
+    if (id == UNASSIGNED_LAYER_ID) {
+        return nullptr;
+    }
+    auto it = mIdToLayer.find(id);
+    if (it == mIdToLayer.end()) {
+        return nullptr;
+    }
+    return &it->second.references;
+}
+
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+    if (layerId == UNASSIGNED_LAYER_ID) {
+        return UNASSIGNED_LAYER_ID;
+    }
+
+    std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+    if (!linkedLayers) {
+        ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+              layerToLink);
+        return UNASSIGNED_LAYER_ID;
+    }
+    linkedLayers->emplace_back(layerToLink);
+    return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+    std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+    if (!linkedLayers) {
+        return UNASSIGNED_LAYER_ID;
+    }
+    swapErase(*linkedLayers, linkedLayer);
+    return UNASSIGNED_LAYER_ID;
+}
+
+std::vector<uint32_t> LayerLifecycleManager::unlinkLayers(const std::vector<uint32_t>& layerIds,
+                                                          uint32_t linkedLayer) {
+    for (uint32_t layerId : layerIds) {
+        unlinkLayer(layerId, linkedLayer);
+    }
+    return {};
+}
+
+std::string LayerLifecycleManager::References::getDebugString() const {
+    std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:";
+    std::for_each(references.begin(), references.end(),
+                  [&debugInfo = debugInfo](const uint32_t& reference) mutable {
+                      debugInfo += std::to_string(reference) + ",";
+                  });
+    return debugInfo;
+}
+
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+    auto it = mIdToLayer.find(relativeRootId);
+    if (it == mIdToLayer.end()) {
+        return;
+    }
+    RequestedLayerState& layer = it->second.owner;
+    layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+    layer.changes |=
+            RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+    mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
+// Some layers mirror the entire display stack. Since we don't have a single root layer per display
+// we have to track all these layers and update what they mirror when the list of root layers
+// on a display changes. This function walks through the list of display mirroring layers
+// and updates its list of layers that its mirroring. This function should be called when a new
+// root layer is added, removed or moved to another display.
+void LayerLifecycleManager::updateDisplayMirrorLayers(RequestedLayerState& rootLayer) {
+    for (uint32_t mirrorLayerId : mDisplayMirroringLayers) {
+        RequestedLayerState* mirrorLayer = getLayerFromId(mirrorLayerId);
+        bool canBeMirrored =
+                rootLayer.isRoot() && rootLayer.layerStack == mirrorLayer->layerStackToMirror;
+        bool currentlyMirrored =
+                std::find(mirrorLayer->mirrorIds.begin(), mirrorLayer->mirrorIds.end(),
+                          rootLayer.id) != mirrorLayer->mirrorIds.end();
+
+        if (canBeMirrored && !currentlyMirrored) {
+            mirrorLayer->mirrorIds.emplace_back(rootLayer.id);
+            linkLayer(rootLayer.id, mirrorLayer->id);
+            mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+        } else if (!canBeMirrored && currentlyMirrored) {
+            swapErase(mirrorLayer->mirrorIds, rootLayer.id);
+            unlinkLayer(rootLayer.id, mirrorLayer->id);
+            mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+        }
+    }
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
new file mode 100644
index 0000000..f0d2c22
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -0,0 +1,116 @@
+/*
+ * 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 "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Owns a collection of RequestedLayerStates and manages their lifecycle
+// and state changes.
+//
+// RequestedLayerStates are tracked and destroyed if they have no parent and
+// no handle left to keep them alive. The handle does not keep a reference to
+// the RequestedLayerState but a layer id associated with the RequestedLayerState.
+// If the handle is destroyed and the RequestedLayerState does not have a parent,
+// the LayerLifecycleManager destroys the RequestedLayerState.
+//
+// Threading: This class is not thread safe, it requires external synchronization.
+//
+// Typical usage: Input states (new layers, transactions, destroyed layer handles)
+// are collected in the background passed into the LayerLifecycleManager to update
+// layer lifecycle and layer state at start of composition.
+class LayerLifecycleManager {
+public:
+    // External state changes should be updated in the following order:
+    void addLayers(std::vector<std::unique_ptr<RequestedLayerState>>);
+    // Ignore unknown layers when interoping with legacy front end. In legacy we destroy
+    // the layers it is unreachable. When using the LayerLifecycleManager for layer trace
+    // generation we may encounter layers which are known because we don't have an explicit
+    // lifecycle. Ignore these errors while we have to interop with legacy.
+    void applyTransactions(const std::vector<TransactionState>&, bool ignoreUnknownLayers = false);
+    // Ignore unknown handles when iteroping with legacy front end. In the old world, we
+    // would create child layers which are not necessary with the new front end. This means
+    // we will get notified for handle changes that don't exist in the new front end.
+    void onHandlesDestroyed(const std::vector<uint32_t>&, bool ignoreUnknownHandles = false);
+
+    // Detaches the layer from its relative parent to prevent a loop in the
+    // layer hierarchy. This overrides the RequestedLayerState and leaves
+    // the system in an invalid state. This is always a client error that
+    // needs to be fixed but overriding the state allows us to fail gracefully.
+    void fixRelativeZLoop(uint32_t relativeRootId);
+
+    // Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
+    // ILifecycleListener callbacks and clears any change flags from previous state
+    // updates. This function should be called outside the hot path since it's not
+    // critical to composition.
+    void commitChanges();
+
+    class ILifecycleListener {
+    public:
+        virtual ~ILifecycleListener() = default;
+        // Called on commitChanges when a layer is added. The callback includes
+        // the layer state the client was created with as well as any state updates
+        // until changes were committed.
+        virtual void onLayerAdded(const RequestedLayerState&) = 0;
+        // Called on commitChanges when a layer has been destroyed. The callback
+        // includes the final state before the layer was destroyed.
+        virtual void onLayerDestroyed(const RequestedLayerState&) = 0;
+    };
+    void addLifecycleListener(std::shared_ptr<ILifecycleListener>);
+    void removeLifecycleListener(std::shared_ptr<ILifecycleListener>);
+    const std::vector<std::unique_ptr<RequestedLayerState>>& getLayers() const;
+    const std::vector<std::unique_ptr<RequestedLayerState>>& getDestroyedLayers() const;
+    const ftl::Flags<RequestedLayerState::Changes> getGlobalChanges() const;
+
+private:
+    friend class LayerLifecycleManagerTest;
+    friend class HierarchyBuilderTest;
+    friend class android::SurfaceFlinger;
+
+    RequestedLayerState* getLayerFromId(uint32_t);
+    std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
+    uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+    uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
+    std::vector<uint32_t> unlinkLayers(const std::vector<uint32_t>& layerIds, uint32_t linkedLayer);
+
+    void updateDisplayMirrorLayers(RequestedLayerState& rootLayer);
+
+    struct References {
+        // Lifetime tied to mLayers
+        RequestedLayerState& owner;
+        std::vector<uint32_t> references;
+        std::string getDebugString() const;
+    };
+    std::unordered_map<uint32_t, References> mIdToLayer;
+    // Listeners are invoked once changes are committed.
+    std::vector<std::shared_ptr<ILifecycleListener>> mListeners;
+    // Layers that mirror a display stack (see updateDisplayMirrorLayers)
+    std::vector<uint32_t> mDisplayMirroringLayers;
+
+    // Aggregation of changes since last commit.
+    ftl::Flags<RequestedLayerState::Changes> mGlobalChanges;
+    std::vector<std::unique_ptr<RequestedLayerState>> mLayers;
+    // Layers pending destruction. Layers will be destroyed once changes are committed.
+    std::vector<std::unique_ptr<RequestedLayerState>> mDestroyedLayers;
+    // Keeps track of all the layers that were added in order. Changes will be cleared once
+    // committed.
+    std::vector<RequestedLayerState*> mAddedLayers;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
new file mode 100644
index 0000000..4943483
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+// Uncomment to trace layer updates for a single layer
+// #define LOG_LAYER 1
+
+#ifdef LOG_LAYER
+#define LLOGV(LAYER_ID, x, ...) \
+    ALOGV_IF(((LAYER_ID) == LOG_LAYER), "[%d] %s " x, LOG_LAYER, __func__, ##__VA_ARGS__);
+#else
+#define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
+#endif
+
+#define LLOGD(LAYER_ID, x, ...) ALOGD("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
new file mode 100644
index 0000000..a992584
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerSnapshot"
+
+#include "LayerSnapshot.h"
+
+namespace android::surfaceflinger::frontend {
+
+using namespace ftl::flag_operators;
+
+LayerSnapshot::LayerSnapshot(const RequestedLayerState& state,
+                             const LayerHierarchy::TraversalPath& path)
+      : path(path) {
+    // Provide a unique id for all snapshots.
+    // A front end layer can generate multiple snapshots if its mirrored.
+    // Additionally, if the layer is not reachable, we may choose to destroy
+    // and recreate the snapshot in which case the unique sequence id will
+    // change. The consumer shouldn't tie any lifetimes to this unique id but
+    // register a LayerLifecycleManager::ILifecycleListener or get a list of
+    // destroyed layers from LayerLifecycleManager.
+    if (path.isClone()) {
+        uniqueSequence =
+                LayerCreationArgs::getInternalLayerId(LayerCreationArgs::sInternalSequence++);
+    } else {
+        uniqueSequence = state.id;
+    }
+    sequence = static_cast<int32_t>(state.id);
+    name = state.name;
+    textureName = state.textureName;
+    premultipliedAlpha = state.premultipliedAlpha;
+    inputInfo.name = state.name;
+    inputInfo.id = static_cast<int32_t>(uniqueSequence);
+    inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid);
+    inputInfo.ownerPid = state.ownerPid;
+    uid = state.ownerUid;
+    pid = state.ownerPid;
+    changes = RequestedLayerState::Changes::Created;
+    mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
+            ? path
+            : LayerHierarchy::TraversalPath::ROOT;
+}
+
+// As documented in libhardware header, formats in the range
+// 0x100 - 0x1FF are specific to the HAL implementation, and
+// are known to have no alpha channel
+// TODO: move definition for device-specific range into
+// hardware.h, instead of using hard-coded values here.
+#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
+
+bool LayerSnapshot::isOpaqueFormat(PixelFormat format) {
+    if (HARDWARE_IS_DEVICE_FORMAT(format)) {
+        return true;
+    }
+    switch (format) {
+        case PIXEL_FORMAT_RGBA_8888:
+        case PIXEL_FORMAT_BGRA_8888:
+        case PIXEL_FORMAT_RGBA_FP16:
+        case PIXEL_FORMAT_RGBA_1010102:
+        case PIXEL_FORMAT_R_8:
+            return false;
+    }
+    // in all other case, we have no blending (also for unknown formats)
+    return true;
+}
+
+bool LayerSnapshot::hasBufferOrSidebandStream() const {
+    return ((sidebandStream != nullptr) || (externalTexture != nullptr));
+}
+
+bool LayerSnapshot::drawShadows() const {
+    return shadowSettings.length > 0.f;
+}
+
+bool LayerSnapshot::fillsColor() const {
+    return !hasBufferOrSidebandStream() && color.r >= 0.0_hf && color.g >= 0.0_hf &&
+            color.b >= 0.0_hf;
+}
+
+bool LayerSnapshot::hasBlur() const {
+    return backgroundBlurRadius > 0 || blurRegions.size() > 0;
+}
+
+bool LayerSnapshot::hasEffect() const {
+    return fillsColor() || drawShadows() || hasBlur();
+}
+
+bool LayerSnapshot::hasSomethingToDraw() const {
+    return hasEffect() || hasBufferOrSidebandStream();
+}
+
+bool LayerSnapshot::isContentOpaque() const {
+    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+    // layer's opaque flag.
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    // if the layer has the opaque flag, then we're always opaque
+    if (layerOpaqueFlagSet) {
+        return true;
+    }
+
+    // If the buffer has no alpha channel, then we are opaque
+    if (hasBufferOrSidebandStream() &&
+        isOpaqueFormat(externalTexture ? externalTexture->getPixelFormat() : PIXEL_FORMAT_NONE)) {
+        return true;
+    }
+
+    // Lastly consider the layer opaque if drawing a color with alpha == 1.0
+    return fillsColor() && color.a == 1.0_hf;
+}
+
+bool LayerSnapshot::isHiddenByPolicy() const {
+    return invalidTransform || isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent;
+}
+
+bool LayerSnapshot::getIsVisible() const {
+    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
+        return false;
+    }
+
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    if (isHiddenByPolicy()) {
+        return false;
+    }
+
+    return color.a > 0.0f || hasBlur();
+}
+
+std::string LayerSnapshot::getIsVisibleReason() const {
+    // not visible
+    if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+    if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
+    if (invalidTransform) return "invalidTransform";
+    if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
+    if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+    if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
+
+    // visible
+    std::stringstream reason;
+    if (sidebandStream != nullptr) reason << " sidebandStream";
+    if (externalTexture != nullptr)
+        reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber;
+    if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
+    if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
+    if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
+    if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+    return reason.str();
+}
+
+bool LayerSnapshot::canReceiveInput() const {
+    return !isHiddenByPolicy() && (!hasBufferOrSidebandStream() || color.a > 0.0f);
+}
+
+bool LayerSnapshot::isTransformValid(const ui::Transform& t) {
+    float transformDet = t.det();
+    return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
+}
+
+bool LayerSnapshot::hasInputInfo() const {
+    return inputInfo.token != nullptr ||
+            inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
+std::string LayerSnapshot::getDebugString() const {
+    std::stringstream debug;
+    debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
+          << getIsVisibleReason() << "} changes=" << changes.string()
+          << " layerStack=" << outputFilter.layerStack.id << " geomLayerBounds={"
+          << geomLayerBounds.left << "," << geomLayerBounds.top << "," << geomLayerBounds.bottom
+          << "," << geomLayerBounds.right << "}"
+          << " geomLayerTransform={tx=" << geomLayerTransform.tx()
+          << ",ty=" << geomLayerTransform.ty() << "}"
+          << "}";
+    debug << " input{ touchCropId=" << touchCropId
+          << " replaceTouchableRegionWithCrop=" << inputInfo.replaceTouchableRegionWithCrop << "}";
+    return debug.str();
+}
+
+FloatRect LayerSnapshot::sourceBounds() const {
+    if (!externalTexture) {
+        return geomLayerBounds;
+    }
+    return geomBufferSize.toFloatRect();
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
new file mode 100644
index 0000000..b167d3e
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -0,0 +1,121 @@
+/*
+ * 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 <compositionengine/LayerFECompositionState.h>
+#include <renderengine/LayerSettings.h>
+#include "LayerHierarchy.h"
+#include "RequestedLayerState.h"
+#include "Scheduler/LayerInfo.h"
+#include "android-base/stringprintf.h"
+
+namespace android::surfaceflinger::frontend {
+
+struct RoundedCornerState {
+    RoundedCornerState() = default;
+    RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
+          : cropRect(cropRect), radius(radius) {}
+
+    // Rounded rectangle in local layer coordinate space.
+    FloatRect cropRect = FloatRect();
+    // Radius of the rounded rectangle.
+    vec2 radius;
+    bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
+    bool operator==(RoundedCornerState const& rhs) const {
+        return cropRect == rhs.cropRect && radius == rhs.radius;
+    }
+};
+
+struct ChildState {
+    bool hasValidFrameRate = false;
+};
+
+// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
+// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
+// passed to Render Engine are created using properties stored on this struct.
+struct LayerSnapshot : public compositionengine::LayerFECompositionState {
+    LayerSnapshot() = default;
+    LayerSnapshot(const RequestedLayerState&, const LayerHierarchy::TraversalPath&);
+
+    LayerHierarchy::TraversalPath path;
+    size_t globalZ = std::numeric_limits<ssize_t>::max();
+    bool invalidTransform = false;
+    bool isHiddenByPolicyFromParent = false;
+    bool isHiddenByPolicyFromRelativeParent = false;
+    ftl::Flags<RequestedLayerState::Changes> changes;
+    // Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique.
+    // For mirrored layers, snapshots will have the same sequence so this unique id provides
+    // an alternative identifier when needed.
+    uint32_t uniqueSequence;
+    // Layer id used to create this snapshot. Multiple snapshots will have the same sequence if they
+    // generated from the same layer, for example when mirroring.
+    int32_t sequence;
+    std::string name;
+    uint32_t textureName;
+    bool contentOpaque;
+    bool layerOpaqueFlagSet;
+    RoundedCornerState roundedCorner;
+    FloatRect transformedBounds;
+    Rect transformedBoundsWithoutTransparentRegion;
+    renderengine::ShadowSettings shadowSettings;
+    bool premultipliedAlpha;
+    bool isHdrY410;
+    ui::Transform parentTransform;
+    Rect bufferSize;
+    Rect croppedBufferSize;
+    std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+    gui::LayerMetadata layerMetadata;
+    gui::LayerMetadata relativeLayerMetadata;
+    bool hasReadyFrame;
+    ui::Transform localTransformInverse;
+    gui::WindowInfo inputInfo;
+    ui::Transform localTransform;
+    gui::DropInputMode dropInputMode;
+    bool isTrustedOverlay;
+    gui::GameMode gameMode;
+    scheduler::LayerInfo::FrameRate frameRate;
+    ui::Transform::RotationFlags fixedTransformHint;
+    std::optional<ui::Transform::RotationFlags> transformHint;
+    bool handleSkipScreenshotFlag = false;
+    int32_t frameRateSelectionPriority;
+    LayerHierarchy::TraversalPath mirrorRootPath;
+    bool unreachable = true;
+    uint32_t touchCropId;
+    uid_t uid;
+    pid_t pid;
+    ChildState childState;
+
+    static bool isOpaqueFormat(PixelFormat format);
+    static bool isTransformValid(const ui::Transform& t);
+
+    bool canReceiveInput() const;
+    bool drawShadows() const;
+    bool fillsColor() const;
+    bool getIsVisible() const;
+    bool hasBlur() const;
+    bool hasBufferOrSidebandStream() const;
+    bool hasEffect() const;
+    bool hasSomethingToDraw() const;
+    bool isContentOpaque() const;
+    bool isHiddenByPolicy() const;
+    std::string getDebugString() const;
+    std::string getIsVisibleReason() const;
+    bool hasInputInfo() const;
+    FloatRect sourceBounds() const;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
new file mode 100644
index 0000000..985c6f9
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -0,0 +1,1197 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerSnapshotBuilder"
+
+#include "LayerSnapshotBuilder.h"
+#include <gui/TraceUtils.h>
+#include <ui/FloatRect.h>
+
+#include <numeric>
+#include <optional>
+
+#include <gui/TraceUtils.h>
+#include "DisplayHardware/HWC2.h"
+#include "DisplayHardware/Hal.h"
+#include "LayerLog.h"
+#include "LayerSnapshotBuilder.h"
+#include "TimeStats/TimeStats.h"
+#include "ftl/small_map.h"
+
+namespace android::surfaceflinger::frontend {
+
+using namespace ftl::flag_operators;
+
+namespace {
+FloatRect getMaxDisplayBounds(
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+    const ui::Size maxSize = [&displays] {
+        if (displays.empty()) return ui::Size{5000, 5000};
+
+        return std::accumulate(displays.begin(), displays.end(), ui::kEmptySize,
+                               [](ui::Size size, const auto& pair) -> ui::Size {
+                                   const auto& display = pair.second;
+                                   return {std::max(size.getWidth(), display.info.logicalWidth),
+                                           std::max(size.getHeight(), display.info.logicalHeight)};
+                               });
+    }();
+
+    // Ignore display bounds for now since they will be computed later. Use a large Rect bound
+    // to ensure it's bigger than an actual display will be.
+    const float xMax = static_cast<float>(maxSize.getWidth()) * 10.f;
+    const float yMax = static_cast<float>(maxSize.getHeight()) * 10.f;
+
+    return {-xMax, -yMax, xMax, yMax};
+}
+
+// Applies the given transform to the region, while protecting against overflows caused by any
+// offsets. If applying the offset in the transform to any of the Rects in the region would result
+// in an overflow, they are not added to the output Region.
+Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r,
+                                      const std::string& debugWindowName) {
+    // Round the translation using the same rounding strategy used by ui::Transform.
+    const auto tx = static_cast<int32_t>(t.tx() + 0.5);
+    const auto ty = static_cast<int32_t>(t.ty() + 0.5);
+
+    ui::Transform transformWithoutOffset = t;
+    transformWithoutOffset.set(0.f, 0.f);
+
+    const Region transformed = transformWithoutOffset.transform(r);
+
+    // Apply the translation to each of the Rects in the region while discarding any that overflow.
+    Region ret;
+    for (const auto& rect : transformed) {
+        Rect newRect;
+        if (__builtin_add_overflow(rect.left, tx, &newRect.left) ||
+            __builtin_add_overflow(rect.top, ty, &newRect.top) ||
+            __builtin_add_overflow(rect.right, tx, &newRect.right) ||
+            __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) {
+            ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.",
+                  debugWindowName.c_str());
+            continue;
+        }
+        ret.orSelf(newRect);
+    }
+    return ret;
+}
+
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because Layer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform getInputTransform(const LayerSnapshot& snapshot) {
+    if (!snapshot.hasBufferOrSidebandStream()) {
+        return snapshot.geomLayerTransform;
+    }
+    return snapshot.parentTransform;
+}
+
+/**
+ * Returns the bounds used to fill the input frame and the touchable region.
+ *
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds don't include the buffer transform, where the input assumes
+ * that's already included.
+ */
+std::pair<FloatRect, bool> getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) {
+    FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect();
+    if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() &&
+        snapshot.localTransform.getType() != ui::Transform::IDENTITY) {
+        inputBounds = snapshot.localTransform.transform(inputBounds);
+    }
+
+    bool inputBoundsValid = snapshot.croppedBufferSize.isValid();
+    if (!inputBoundsValid) {
+        /**
+         * Input bounds are based on the layer crop or buffer size. But if we are using
+         * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
+         * we can use the parent bounds as the input bounds if the layer does not have buffer
+         * or a crop. We want to unify this logic but because of compat reasons we cannot always
+         * use the parent bounds. A layer without a buffer can get input. So when a window is
+         * initially added, its touchable region can fill its parent layer bounds and that can
+         * have negative consequences.
+         */
+        inputBounds = fillParentBounds ? snapshot.geomLayerBounds : FloatRect{};
+    }
+
+    // Clamp surface inset to the input bounds.
+    const float inset = static_cast<float>(snapshot.inputInfo.surfaceInset);
+    const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
+    const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
+
+    // Apply the insets to the input bounds.
+    inputBounds.left += xSurfaceInset;
+    inputBounds.top += ySurfaceInset;
+    inputBounds.right -= xSurfaceInset;
+    inputBounds.bottom -= ySurfaceInset;
+    return {inputBounds, inputBoundsValid};
+}
+
+Rect getInputBoundsInDisplaySpace(const LayerSnapshot& snapshot, const FloatRect& insetBounds,
+                                  const ui::Transform& screenToDisplay) {
+    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
+    // frame and transform used for the layer, which determines the bounds and the coordinate space
+    // within which the layer will receive input.
+
+    // Coordinate space definitions:
+    //   - display: The display device's coordinate space. Correlates to pixels on the display.
+    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
+    //   - layer: The coordinate space of this layer.
+    //   - input: The coordinate space in which this layer will receive input events. This could be
+    //            different than layer space if a surfaceInset is used, which changes the origin
+    //            of the input space.
+
+    // Crop the input bounds to ensure it is within the parent's bounds.
+    const FloatRect croppedInsetBoundsInLayer = snapshot.geomLayerBounds.intersect(insetBounds);
+
+    const ui::Transform layerToScreen = getInputTransform(snapshot);
+    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
+
+    return Rect{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+}
+
+void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& screenToDisplay,
+                        const LayerSnapshot& snapshot) {
+    auto [inputBounds, inputBoundsValid] = getInputBounds(snapshot, /*fillParentBounds=*/false);
+    if (!inputBoundsValid) {
+        info.touchableRegion.clear();
+    }
+
+    const Rect roundedFrameInDisplay =
+            getInputBoundsInDisplaySpace(snapshot, inputBounds, screenToDisplay);
+    info.frameLeft = roundedFrameInDisplay.left;
+    info.frameTop = roundedFrameInDisplay.top;
+    info.frameRight = roundedFrameInDisplay.right;
+    info.frameBottom = roundedFrameInDisplay.bottom;
+
+    ui::Transform inputToLayer;
+    inputToLayer.set(inputBounds.left, inputBounds.top);
+    const ui::Transform layerToScreen = getInputTransform(snapshot);
+    const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
+
+    // InputDispatcher expects a display-to-input transform.
+    info.transform = inputToDisplay.inverse();
+
+    // The touchable region is specified in the input coordinate space. Change it to display space.
+    info.touchableRegion =
+            transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, snapshot.name);
+}
+
+void handleDropInputMode(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot) {
+    if (snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
+        return;
+    }
+
+    // Check if we need to drop input unconditionally
+    const gui::DropInputMode dropInputMode = snapshot.dropInputMode;
+    if (dropInputMode == gui::DropInputMode::ALL) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy.", snapshot.name.c_str());
+        return;
+    }
+
+    // Check if we need to check if the window is obscured by parent
+    if (dropInputMode != gui::DropInputMode::OBSCURED) {
+        return;
+    }
+
+    // Check if the parent has set an alpha on the layer
+    if (parentSnapshot.color.a != 1.0_hf) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy because alpha=%f",
+              snapshot.name.c_str(), static_cast<float>(parentSnapshot.color.a));
+    }
+
+    // Check if the parent has cropped the buffer
+    Rect bufferSize = snapshot.croppedBufferSize;
+    if (!bufferSize.isValid()) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
+        return;
+    }
+
+    // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
+    // To check if the layer has been cropped, we take the buffer bounds, apply the local
+    // layer crop and apply the same set of transforms to move to screenspace. If the bounds
+    // match then the layer has not been cropped by its parents.
+    Rect bufferInScreenSpace(snapshot.geomLayerTransform.transform(bufferSize));
+    bool croppedByParent = bufferInScreenSpace != Rect{snapshot.transformedBounds};
+
+    if (croppedByParent) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
+        ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
+              snapshot.name.c_str());
+    } else {
+        // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
+        // input if the window is obscured. This check should be done in surfaceflinger but the
+        // logic currently resides in inputflinger. So pass the if_obscured check to input to only
+        // drop input events if the window is obscured.
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
+    }
+}
+
+auto getBlendMode(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
+    auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+    if (snapshot.alpha != 1.0f || !snapshot.isContentOpaque()) {
+        blendMode = requested.premultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+                                                 : Hwc2::IComposerClient::BlendMode::COVERAGE;
+    }
+    return blendMode;
+}
+
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+                         bool forceFullDamage, Region& outSurfaceDamageRegion) {
+    if (!hasReadyFrame) {
+        outSurfaceDamageRegion.clear();
+        return;
+    }
+    if (forceFullDamage) {
+        outSurfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        outSurfaceDamageRegion = requested.surfaceDamageRegion;
+    }
+}
+
+void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+    snapshot.isVisible = visible;
+
+    // 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);
+}
+
+bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
+    if (requested.potentialCursor) {
+        return false;
+    }
+
+    if (snapshot.inputInfo.token != nullptr) {
+        return true;
+    }
+
+    if (snapshot.hasBufferOrSidebandStream()) {
+        return true;
+    }
+
+    return requested.windowInfoHandle &&
+            requested.windowInfoHandle->getInfo()->inputConfig.test(
+                    gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
+void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+                    const LayerSnapshotBuilder::Args& args) {
+    snapshot.metadata.clear();
+    for (const auto& [key, mandatory] : args.supportedLayerGenericMetadata) {
+        auto compatIter = args.genericLayerMetadataKeyMap.find(key);
+        if (compatIter == std::end(args.genericLayerMetadataKeyMap)) {
+            continue;
+        }
+        const uint32_t id = compatIter->second;
+        auto it = requested.metadata.mMap.find(id);
+        if (it == std::end(requested.metadata.mMap)) {
+            continue;
+        }
+
+        snapshot.metadata.emplace(key,
+                                  compositionengine::GenericLayerMetadataEntry{mandatory,
+                                                                               it->second});
+    }
+}
+
+void clearChanges(LayerSnapshot& snapshot) {
+    snapshot.changes.clear();
+    snapshot.contentDirty = false;
+    snapshot.hasReadyFrame = false;
+    snapshot.sidebandStreamHasFrame = false;
+    snapshot.surfaceDamage.clear();
+}
+
+} // namespace
+
+LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
+    LayerSnapshot snapshot;
+    snapshot.path = LayerHierarchy::TraversalPath::ROOT;
+    snapshot.changes = ftl::Flags<RequestedLayerState::Changes>();
+    snapshot.isHiddenByPolicyFromParent = false;
+    snapshot.isHiddenByPolicyFromRelativeParent = false;
+    snapshot.parentTransform.reset();
+    snapshot.geomLayerTransform.reset();
+    snapshot.geomInverseLayerTransform.reset();
+    snapshot.geomLayerBounds = getMaxDisplayBounds({});
+    snapshot.roundedCorner = RoundedCornerState();
+    snapshot.stretchEffect = {};
+    snapshot.outputFilter.layerStack = ui::DEFAULT_LAYER_STACK;
+    snapshot.outputFilter.toInternalDisplay = false;
+    snapshot.isSecure = false;
+    snapshot.color.a = 1.0_hf;
+    snapshot.colorTransformIsIdentity = true;
+    snapshot.shadowRadius = 0.f;
+    snapshot.layerMetadata.mMap.clear();
+    snapshot.relativeLayerMetadata.mMap.clear();
+    snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
+    snapshot.dropInputMode = gui::DropInputMode::NONE;
+    snapshot.isTrustedOverlay = false;
+    snapshot.gameMode = gui::GameMode::Unsupported;
+    snapshot.frameRate = {};
+    snapshot.fixedTransformHint = ui::Transform::ROT_INVALID;
+    return snapshot;
+}
+
+LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {}
+
+LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() {
+    args.forceUpdate = ForceUpdateFlags::ALL;
+    updateSnapshots(args);
+}
+
+bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
+    if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) {
+        // force update requested, or we have display changes, so skip the fast path
+        return false;
+    }
+
+    if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
+        return true;
+    }
+
+    if (args.layerLifecycleManager.getGlobalChanges() != RequestedLayerState::Changes::Content) {
+        // We have changes that require us to walk the hierarchy and update child layers.
+        // No fast path for you.
+        return false;
+    }
+
+    // There are only content changes which do not require any child layer snapshots to be updated.
+    ALOGV("%s", __func__);
+    ATRACE_NAME("FastPath");
+
+    // Collect layers with changes
+    ftl::SmallMap<uint32_t, RequestedLayerState*, 10> layersWithChanges;
+    for (auto& layer : args.layerLifecycleManager.getLayers()) {
+        if (layer->changes.test(RequestedLayerState::Changes::Content)) {
+            layersWithChanges.emplace_or_replace(layer->id, layer.get());
+        }
+    }
+
+    // Walk through the snapshots, clearing previous change flags and updating the snapshots
+    // if needed.
+    for (auto& snapshot : mSnapshots) {
+        auto it = layersWithChanges.find(snapshot->path.id);
+        if (it != layersWithChanges.end()) {
+            ALOGV("%s fast path snapshot changes = %s", __func__,
+                  mRootSnapshot.changes.string().c_str());
+            LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
+            updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
+        }
+    }
+    return true;
+}
+
+void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
+    ATRACE_NAME("UpdateSnapshots");
+    if (args.parentCrop) {
+        mRootSnapshot.geomLayerBounds = *args.parentCrop;
+    } else if (args.forceUpdate == ForceUpdateFlags::ALL || args.displayChanges) {
+        mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
+    }
+    if (args.displayChanges) {
+        mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren |
+                RequestedLayerState::Changes::Geometry;
+    }
+    if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) {
+        mRootSnapshot.changes |=
+                RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility;
+    }
+    LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
+    if (args.root.getLayer()) {
+        // The hierarchy can have a root layer when used for screenshots otherwise, it will have
+        // multiple children.
+        LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id,
+                                                                LayerHierarchy::Variant::Attached);
+        updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot);
+    } else {
+        for (auto& [childHierarchy, variant] : args.root.mChildren) {
+            LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root,
+                                                                    childHierarchy->getLayer()->id,
+                                                                    variant);
+            updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot);
+        }
+    }
+
+    // Update touchable region crops outside the main update pass. This is because a layer could be
+    // cropped by any other layer and it requires both snapshots to be updated.
+    updateTouchableRegionCrop(args);
+
+    const bool hasUnreachableSnapshots = sortSnapshotsByZ(args);
+    clearChanges(mRootSnapshot);
+
+    // Destroy unreachable snapshots for clone layers. And destroy snapshots for non-clone
+    // layers if the layer have been destroyed.
+    // TODO(b/238781169) consider making clone layer ids stable as well
+    if (!hasUnreachableSnapshots && args.layerLifecycleManager.getDestroyedLayers().empty()) {
+        return;
+    }
+
+    std::unordered_set<uint32_t> destroyedLayerIds;
+    for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) {
+        destroyedLayerIds.insert(destroyedLayer->id);
+    }
+
+    auto it = mSnapshots.begin();
+    while (it < mSnapshots.end()) {
+        auto& traversalPath = it->get()->path;
+        if (!it->get()->unreachable &&
+            destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) {
+            it++;
+            continue;
+        }
+
+        mIdToSnapshot.erase(traversalPath);
+        mNeedsTouchableRegionCrop.erase(traversalPath);
+        mSnapshots.back()->globalZ = it->get()->globalZ;
+        std::iter_swap(it, mSnapshots.end() - 1);
+        mSnapshots.erase(mSnapshots.end() - 1);
+    }
+}
+
+void LayerSnapshotBuilder::update(const Args& args) {
+    for (auto& snapshot : mSnapshots) {
+        clearChanges(*snapshot);
+    }
+
+    if (tryFastUpdate(args)) {
+        return;
+    }
+    updateSnapshots(args);
+}
+
+const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
+        const Args& args, const LayerHierarchy& hierarchy,
+        LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot) {
+    const RequestedLayerState* layer = hierarchy.getLayer();
+    LayerSnapshot* snapshot = getSnapshot(traversalPath);
+    const bool newSnapshot = snapshot == nullptr;
+    if (newSnapshot) {
+        snapshot = createSnapshot(traversalPath, *layer, parentSnapshot);
+    }
+    scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate;
+    if (traversalPath.isRelative()) {
+        bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
+        updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
+    } else {
+        if (traversalPath.isAttached()) {
+            resetRelativeState(*snapshot);
+        }
+        updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
+    }
+
+    for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
+        LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
+                                                                childHierarchy->getLayer()->id,
+                                                                variant);
+        const LayerSnapshot& childSnapshot =
+                updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+        updateChildState(*snapshot, childSnapshot, args);
+    }
+
+    if (oldFrameRate == snapshot->frameRate) {
+        snapshot->changes.clear(RequestedLayerState::Changes::FrameRate);
+    }
+    return *snapshot;
+}
+
+LayerSnapshot* LayerSnapshotBuilder::getSnapshot(uint32_t layerId) const {
+    if (layerId == UNASSIGNED_LAYER_ID) {
+        return nullptr;
+    }
+    LayerHierarchy::TraversalPath path{.id = layerId};
+    return getSnapshot(path);
+}
+
+LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::TraversalPath& id) const {
+    auto it = mIdToSnapshot.find(id);
+    return it == mIdToSnapshot.end() ? nullptr : it->second;
+}
+
+LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path,
+                                                    const RequestedLayerState& layer,
+                                                    const LayerSnapshot& parentSnapshot) {
+    mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, path));
+    LayerSnapshot* snapshot = mSnapshots.back().get();
+    snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
+    if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
+        snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
+    }
+    mIdToSnapshot[path] = snapshot;
+    return snapshot;
+}
+
+bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
+    if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE &&
+        !args.layerLifecycleManager.getGlobalChanges().any(
+                RequestedLayerState::Changes::Hierarchy |
+                RequestedLayerState::Changes::Visibility)) {
+        // We are not force updating and there are no hierarchy or visibility changes. Avoid sorting
+        // the snapshots.
+        return false;
+    }
+    mResortSnapshots = false;
+
+    for (auto& snapshot : mSnapshots) {
+        snapshot->unreachable = snapshot->path.isClone();
+    }
+
+    size_t globalZ = 0;
+    args.root.traverseInZOrder(
+            [this, &globalZ](const LayerHierarchy&,
+                             const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+                LayerSnapshot* snapshot = getSnapshot(traversalPath);
+                if (!snapshot) {
+                    return false;
+                }
+
+                snapshot->unreachable = false;
+                if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
+                    updateVisibility(*snapshot, snapshot->getIsVisible());
+                    size_t oldZ = snapshot->globalZ;
+                    size_t newZ = globalZ++;
+                    snapshot->globalZ = newZ;
+                    if (oldZ == newZ) {
+                        return true;
+                    }
+                    mSnapshots[newZ]->globalZ = oldZ;
+                    LLOGV(snapshot->sequence, "Made visible z=%zu -> %zu %s", oldZ, newZ,
+                          snapshot->getDebugString().c_str());
+                    std::iter_swap(mSnapshots.begin() + static_cast<ssize_t>(oldZ),
+                                   mSnapshots.begin() + static_cast<ssize_t>(newZ));
+                }
+                return true;
+            });
+    mNumInterestingSnapshots = (int)globalZ;
+    bool hasUnreachableSnapshots = false;
+    while (globalZ < mSnapshots.size()) {
+        mSnapshots[globalZ]->globalZ = globalZ;
+        /* mark unreachable snapshots as explicitly invisible */
+        updateVisibility(*mSnapshots[globalZ], false);
+        if (mSnapshots[globalZ]->unreachable) {
+            hasUnreachableSnapshots = true;
+        }
+        globalZ++;
+    }
+    return hasUnreachableSnapshots;
+}
+
+void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot,
+                                               const LayerSnapshot& parentSnapshot,
+                                               bool parentIsRelative, const Args& args) {
+    if (parentIsRelative) {
+        snapshot.isHiddenByPolicyFromRelativeParent =
+                parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform;
+        if (args.includeMetadata) {
+            snapshot.relativeLayerMetadata = parentSnapshot.layerMetadata;
+        }
+    } else {
+        snapshot.isHiddenByPolicyFromRelativeParent =
+                parentSnapshot.isHiddenByPolicyFromRelativeParent;
+        if (args.includeMetadata) {
+            snapshot.relativeLayerMetadata = parentSnapshot.relativeLayerMetadata;
+        }
+    }
+    snapshot.isVisible = snapshot.getIsVisible();
+}
+
+void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
+                                            const LayerSnapshot& childSnapshot, const Args& args) {
+    if (snapshot.childState.hasValidFrameRate) {
+        return;
+    }
+    if (args.forceUpdate == ForceUpdateFlags::ALL ||
+        childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
+        // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes
+        // for the same reason we are allowing touch boost for those layers. See
+        // RefreshRateSelector::rankFrameRates for details.
+        using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+        const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
+                childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+        const auto layerVotedWithNoVote =
+                childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
+        const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
+                childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+
+        snapshot.childState.hasValidFrameRate |= layerVotedWithDefaultCompatibility ||
+                layerVotedWithNoVote || layerVotedWithExactCompatibility;
+
+        // If we don't have a valid frame rate, but the children do, we set this
+        // layer as NoVote to allow the children to control the refresh rate
+        if (!snapshot.frameRate.rate.isValid() &&
+            snapshot.frameRate.type != FrameRateCompatibility::NoVote &&
+            snapshot.childState.hasValidFrameRate) {
+            snapshot.frameRate =
+                    scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+            snapshot.changes |= childSnapshot.changes & RequestedLayerState::Changes::FrameRate;
+        }
+    }
+}
+
+void LayerSnapshotBuilder::resetRelativeState(LayerSnapshot& snapshot) {
+    snapshot.isHiddenByPolicyFromRelativeParent = false;
+    snapshot.relativeLayerMetadata.mMap.clear();
+}
+
+// TODO (b/259407931): Remove.
+uint32_t getPrimaryDisplayRotationFlags(
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays) {
+    for (auto& [_, display] : displays) {
+        if (display.isPrimary) {
+            return display.rotationFlags;
+        }
+    }
+    return 0;
+}
+
+void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
+                                          const RequestedLayerState& requested,
+                                          const LayerSnapshot& parentSnapshot,
+                                          const LayerHierarchy::TraversalPath& path) {
+    // Always update flags and visibility
+    ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
+            (RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
+             RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
+             RequestedLayerState::Changes::AffectsChildren |
+             RequestedLayerState::Changes::FrameRate);
+    snapshot.changes |= parentChanges | requested.changes;
+    snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
+            parentSnapshot.invalidTransform || requested.isHiddenByPolicy() ||
+            (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end());
+    snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
+    // TODO(b/238781169) scope down the changes to only buffer updates.
+    snapshot.hasReadyFrame = requested.hasReadyFrame();
+    snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame();
+    updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
+                        snapshot.surfaceDamage);
+    snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT
+            ? requested.layerStack
+            : parentSnapshot.outputFilter.layerStack;
+
+    uint32_t primaryDisplayRotationFlags = getPrimaryDisplayRotationFlags(args.displays);
+    const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL ||
+            snapshot.changes.any(RequestedLayerState::Changes::Visibility |
+                                 RequestedLayerState::Changes::Created);
+
+    // always update the buffer regardless of visibility
+    if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) {
+        snapshot.acquireFence =
+                (requested.externalTexture &&
+                 requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+                ? requested.bufferData->acquireFence
+                : Fence::NO_FENCE;
+        snapshot.buffer =
+                requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+        snapshot.bufferSize = requested.getBufferSize(primaryDisplayRotationFlags);
+        snapshot.geomBufferSize = snapshot.bufferSize;
+        snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
+        snapshot.dataspace = requested.dataspace;
+        snapshot.externalTexture = requested.externalTexture;
+        snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+        snapshot.geomBufferTransform = requested.bufferTransform;
+        snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+        snapshot.geomContentCrop = requested.getBufferCrop();
+        snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
+        snapshot.hasProtectedContent = requested.externalTexture &&
+                requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+        snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+                requested.api == NATIVE_WINDOW_API_MEDIA &&
+                requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+        snapshot.sidebandStream = requested.sidebandStream;
+        snapshot.transparentRegionHint = requested.transparentRegion;
+        snapshot.color.rgb = requested.getColor().rgb;
+        snapshot.currentHdrSdrRatio = requested.currentHdrSdrRatio;
+        snapshot.desiredHdrSdrRatio = requested.desiredHdrSdrRatio;
+    }
+
+    if (snapshot.isHiddenByPolicyFromParent &&
+        !snapshot.changes.test(RequestedLayerState::Changes::Created)) {
+        if (forceUpdate ||
+            snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
+                                 RequestedLayerState::Changes::Geometry |
+                                 RequestedLayerState::Changes::Input)) {
+            updateInput(snapshot, requested, parentSnapshot, path, args);
+        }
+        return;
+    }
+
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
+        // If root layer, use the layer stack otherwise get the parent's layer stack.
+        snapshot.color.a = parentSnapshot.color.a * requested.color.a;
+        snapshot.alpha = snapshot.color.a;
+        snapshot.inputInfo.alpha = snapshot.color.a;
+
+        snapshot.isSecure =
+                parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure);
+        snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+        snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay ||
+                (requested.flags & layer_state_t::eLayerSkipScreenshot);
+        snapshot.stretchEffect = (requested.stretchEffect.hasEffect())
+                ? requested.stretchEffect
+                : parentSnapshot.stretchEffect;
+        if (!parentSnapshot.colorTransformIsIdentity) {
+            snapshot.colorTransform = parentSnapshot.colorTransform * requested.colorTransform;
+            snapshot.colorTransformIsIdentity = false;
+        } else {
+            snapshot.colorTransform = requested.colorTransform;
+            snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
+        }
+        snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
+                ? requested.gameMode
+                : parentSnapshot.gameMode;
+        // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+        // marked as skip capture
+        snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+                (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
+    }
+
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren) ||
+        args.displayChanges) {
+        snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
+                ? requested.fixedTransformHint
+                : parentSnapshot.fixedTransformHint;
+
+        if (snapshot.fixedTransformHint != ui::Transform::ROT_INVALID) {
+            snapshot.transformHint = snapshot.fixedTransformHint;
+        } else {
+            const auto display = args.displays.get(snapshot.outputFilter.layerStack);
+            snapshot.transformHint = display.has_value()
+                    ? std::make_optional<>(display->get().transformHint)
+                    : std::nullopt;
+        }
+    }
+
+    if (forceUpdate ||
+        snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
+                             RequestedLayerState::Changes::Hierarchy)) {
+        snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
+                              (requested.requestedFrameRate.type ==
+                               scheduler::LayerInfo::FrameRateCompatibility::NoVote))
+                ? requested.requestedFrameRate
+                : parentSnapshot.frameRate;
+    }
+
+    if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) {
+        updateMetadata(snapshot, requested, args);
+    }
+
+    if (forceUpdate || requested.changes.get() != 0) {
+        snapshot.compositionType = requested.getCompositionType();
+        snapshot.dimmingEnabled = requested.dimmingEnabled;
+        snapshot.layerOpaqueFlagSet =
+                (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+        snapshot.cachingHint = requested.cachingHint;
+        snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority;
+    }
+
+    if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content) ||
+        snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
+        snapshot.color.rgb = requested.getColor().rgb;
+        snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
+        snapshot.backgroundBlurRadius = args.supportsBlur
+                ? static_cast<int>(parentSnapshot.color.a * (float)requested.backgroundBlurRadius)
+                : 0;
+        snapshot.blurRegions = requested.blurRegions;
+        for (auto& region : snapshot.blurRegions) {
+            region.alpha = region.alpha * snapshot.color.a;
+        }
+        snapshot.hdrMetadata = requested.hdrMetadata;
+    }
+
+    if (forceUpdate ||
+        snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
+                             RequestedLayerState::Changes::Geometry)) {
+        updateLayerBounds(snapshot, requested, parentSnapshot, primaryDisplayRotationFlags);
+        updateRoundedCorner(snapshot, requested, parentSnapshot);
+    }
+
+    if (forceUpdate ||
+        snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
+                             RequestedLayerState::Changes::Geometry |
+                             RequestedLayerState::Changes::Input)) {
+        updateInput(snapshot, requested, parentSnapshot, path, args);
+    }
+
+    // computed snapshot properties
+    updateShadows(snapshot, requested, args.globalShadowSettings);
+    if (args.includeMetadata) {
+        snapshot.layerMetadata = parentSnapshot.layerMetadata;
+        snapshot.layerMetadata.merge(requested.metadata);
+    }
+    snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
+            requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
+    snapshot.contentOpaque = snapshot.isContentOpaque();
+    snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() &&
+            snapshot.color.a == 1.f;
+    snapshot.blendMode = getBlendMode(snapshot, requested);
+    LLOGV(snapshot.sequence,
+          "%supdated %s changes:%s parent:%s requested:%s requested:%s from parent %s",
+          args.forceUpdate == ForceUpdateFlags::ALL ? "Force " : "",
+          snapshot.getDebugString().c_str(), snapshot.changes.string().c_str(),
+          parentSnapshot.changes.string().c_str(), requested.changes.string().c_str(),
+          std::to_string(requested.what).c_str(), parentSnapshot.getDebugString().c_str());
+}
+
+void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot,
+                                               const RequestedLayerState& requested,
+                                               const LayerSnapshot& parentSnapshot) {
+    snapshot.roundedCorner = RoundedCornerState();
+    RoundedCornerState parentRoundedCorner;
+    if (parentSnapshot.roundedCorner.hasRoundedCorners()) {
+        parentRoundedCorner = parentSnapshot.roundedCorner;
+        ui::Transform t = snapshot.localTransform.inverse();
+        parentRoundedCorner.cropRect = t.transform(parentRoundedCorner.cropRect);
+        parentRoundedCorner.radius.x *= t.getScaleX();
+        parentRoundedCorner.radius.y *= t.getScaleY();
+    }
+
+    FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect();
+    const vec2 radius(requested.cornerRadius, requested.cornerRadius);
+    RoundedCornerState layerSettings(layerCropRect, radius);
+    const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
+    const bool parentRoundedCornerValid = parentRoundedCorner.hasRoundedCorners();
+    if (layerSettingsValid && parentRoundedCornerValid) {
+        // If the parent and the layer have rounded corner settings, use the parent settings if
+        // the parent crop is entirely inside the layer crop. This has limitations and cause
+        // rendering artifacts. See b/200300845 for correct fix.
+        if (parentRoundedCorner.cropRect.left > layerCropRect.left &&
+            parentRoundedCorner.cropRect.top > layerCropRect.top &&
+            parentRoundedCorner.cropRect.right < layerCropRect.right &&
+            parentRoundedCorner.cropRect.bottom < layerCropRect.bottom) {
+            snapshot.roundedCorner = parentRoundedCorner;
+        } else {
+            snapshot.roundedCorner = layerSettings;
+        }
+    } else if (layerSettingsValid) {
+        snapshot.roundedCorner = layerSettings;
+    } else if (parentRoundedCornerValid) {
+        snapshot.roundedCorner = parentRoundedCorner;
+    }
+}
+
+void LayerSnapshotBuilder::updateLayerBounds(LayerSnapshot& snapshot,
+                                             const RequestedLayerState& requested,
+                                             const LayerSnapshot& parentSnapshot,
+                                             uint32_t primaryDisplayRotationFlags) {
+    snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
+    snapshot.geomCrop = requested.crop;
+    snapshot.localTransform = requested.getTransform(primaryDisplayRotationFlags);
+    snapshot.localTransformInverse = snapshot.localTransform.inverse();
+    snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
+    const bool transformWasInvalid = snapshot.invalidTransform;
+    snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
+    if (snapshot.invalidTransform) {
+        auto& t = snapshot.geomLayerTransform;
+        auto& requestedT = requested.requestedTransform;
+        std::string transformDebug =
+                base::StringPrintf(" transform={%f,%f,%f,%f}  requestedTransform={%f,%f,%f,%f}",
+                                   t.dsdx(), t.dsdy(), t.dtdx(), t.dtdy(), requestedT.dsdx(),
+                                   requestedT.dsdy(), requestedT.dtdx(), requestedT.dtdy());
+        std::string bufferDebug;
+        if (requested.externalTexture) {
+            auto unRotBuffer = requested.getUnrotatedBufferSize(primaryDisplayRotationFlags);
+            auto& destFrame = requested.destinationFrame;
+            bufferDebug = base::StringPrintf(" buffer={%d,%d}  displayRot=%d"
+                                             " destFrame={%d,%d,%d,%d} unRotBuffer={%d,%d}",
+                                             requested.externalTexture->getWidth(),
+                                             requested.externalTexture->getHeight(),
+                                             primaryDisplayRotationFlags, destFrame.left,
+                                             destFrame.top, destFrame.right, destFrame.bottom,
+                                             unRotBuffer.getHeight(), unRotBuffer.getWidth());
+        }
+        ALOGW("Resetting transform for %s because it is invalid.%s%s",
+              snapshot.getDebugString().c_str(), transformDebug.c_str(), bufferDebug.c_str());
+        snapshot.geomLayerTransform.reset();
+    }
+    if (transformWasInvalid != snapshot.invalidTransform) {
+        // If transform is invalid, the layer will be hidden.
+        mResortSnapshots = true;
+    }
+    snapshot.geomInverseLayerTransform = snapshot.geomLayerTransform.inverse();
+
+    FloatRect parentBounds = parentSnapshot.geomLayerBounds;
+    parentBounds = snapshot.localTransform.inverse().transform(parentBounds);
+    snapshot.geomLayerBounds =
+            (requested.externalTexture) ? snapshot.bufferSize.toFloatRect() : parentBounds;
+    if (!requested.crop.isEmpty()) {
+        snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(requested.crop.toFloatRect());
+    }
+    snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds);
+    snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
+    const Rect geomLayerBoundsWithoutTransparentRegion =
+            RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+                                        requested.transparentRegion);
+    snapshot.transformedBoundsWithoutTransparentRegion =
+            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) {
+        snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
+    }
+}
+
+void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot,
+                                         const RequestedLayerState& requested,
+                                         const renderengine::ShadowSettings& globalShadowSettings) {
+    snapshot.shadowRadius = requested.shadowRadius;
+    snapshot.shadowSettings.length = requested.shadowRadius;
+    if (snapshot.shadowRadius > 0.f) {
+        snapshot.shadowSettings = globalShadowSettings;
+
+        // Note: this preserves existing behavior of shadowing the entire layer and not cropping
+        // it if transparent regions are present. This may not be necessary since shadows are
+        // typically cast by layers without transparent regions.
+        snapshot.shadowSettings.boundaries = snapshot.geomLayerBounds;
+
+        // If the casting layer is translucent, we need to fill in the shadow underneath the
+        // layer. Otherwise the generated shadow will only be shown around the casting layer.
+        snapshot.shadowSettings.casterIsTranslucent =
+                !snapshot.isContentOpaque() || (snapshot.alpha < 1.0f);
+        snapshot.shadowSettings.ambientColor *= snapshot.alpha;
+        snapshot.shadowSettings.spotColor *= snapshot.alpha;
+    }
+}
+
+void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
+                                       const RequestedLayerState& requested,
+                                       const LayerSnapshot& parentSnapshot,
+                                       const LayerHierarchy::TraversalPath& path,
+                                       const Args& args) {
+    if (requested.windowInfoHandle) {
+        snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
+    } else {
+        snapshot.inputInfo = {};
+        // b/271132344 revisit this and see if we can always use the layers uid/pid
+        snapshot.inputInfo.name = requested.name;
+        snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid);
+        snapshot.inputInfo.ownerPid = requested.ownerPid;
+    }
+    snapshot.touchCropId = requested.touchCropId;
+
+    snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
+    snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+    updateVisibility(snapshot, snapshot.isVisible);
+    if (!needsInputInfo(snapshot, requested)) {
+        return;
+    }
+
+    static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
+    const std::optional<frontend::DisplayInfo> displayInfoOpt =
+            args.displays.get(snapshot.outputFilter.layerStack);
+    bool noValidDisplay = !displayInfoOpt.has_value();
+    auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
+
+    if (!requested.windowInfoHandle) {
+        snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+    }
+    fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
+
+    if (noValidDisplay) {
+        // Do not let the window receive touches if it is not associated with a valid display
+        // transform. We still allow the window to receive keys and prevent ANRs.
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE;
+    }
+
+    snapshot.inputInfo.alpha = snapshot.color.a;
+    snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
+            ? requested.windowInfoHandle->getInfo()->touchOcclusionMode
+            : parentSnapshot.inputInfo.touchOcclusionMode;
+    if (requested.dropInputMode == gui::DropInputMode::ALL ||
+        parentSnapshot.dropInputMode == gui::DropInputMode::ALL) {
+        snapshot.dropInputMode = gui::DropInputMode::ALL;
+    } else if (requested.dropInputMode == gui::DropInputMode::OBSCURED ||
+               parentSnapshot.dropInputMode == gui::DropInputMode::OBSCURED) {
+        snapshot.dropInputMode = gui::DropInputMode::OBSCURED;
+    } else {
+        snapshot.dropInputMode = gui::DropInputMode::NONE;
+    }
+
+    handleDropInputMode(snapshot, parentSnapshot);
+
+    // If the window will be blacked out on a display because the display does not have the secure
+    // flag and the layer has the secure flag set, then drop input.
+    if (!displayInfo.isSecure && snapshot.isSecure) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
+    }
+
+    auto cropLayerSnapshot = getSnapshot(requested.touchCropId);
+    if (cropLayerSnapshot) {
+        mNeedsTouchableRegionCrop.insert(path);
+    } else if (snapshot.inputInfo.replaceTouchableRegionWithCrop) {
+        FloatRect inputBounds = getInputBounds(snapshot, /*fillParentBounds=*/true).first;
+        Rect inputBoundsInDisplaySpace =
+                getInputBoundsInDisplaySpace(snapshot, inputBounds, displayInfo.transform);
+        snapshot.inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
+    }
+
+    // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
+    // if it was set by WM for a known system overlay
+    if (snapshot.isTrustedOverlay) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY;
+    }
+
+    // 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.
+    if (path.isClone()) {
+        snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
+        // Cloned layers shouldn't handle watch outside since their z order is not determined by
+        // WM or the client.
+        snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
+
+        mNeedsTouchableRegionCrop.insert(path);
+    }
+}
+
+std::vector<std::unique_ptr<LayerSnapshot>>& LayerSnapshotBuilder::getSnapshots() {
+    return mSnapshots;
+}
+
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const ConstVisitor& visitor) const {
+    for (int i = 0; i < mNumInterestingSnapshots; i++) {
+        LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+        if (!snapshot.isVisible) continue;
+        visitor(snapshot);
+    }
+}
+
+// Visit each visible snapshot in z-order
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const ConstVisitor& visitor,
+                                                  const LayerHierarchy& root) const {
+    root.traverseInZOrder(
+            [this, visitor](const LayerHierarchy&,
+                            const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+                LayerSnapshot* snapshot = getSnapshot(traversalPath);
+                if (snapshot && snapshot->isVisible) {
+                    visitor(*snapshot);
+                }
+                return true;
+            });
+}
+
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) {
+    for (int i = 0; i < mNumInterestingSnapshots; i++) {
+        std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
+        if (!snapshot->isVisible) continue;
+        visitor(snapshot);
+    }
+}
+
+void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
+    for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
+        LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+        if (!snapshot.hasInputInfo()) continue;
+        visitor(snapshot);
+    }
+}
+
+void LayerSnapshotBuilder::updateTouchableRegionCrop(const Args& args) {
+    if (mNeedsTouchableRegionCrop.empty()) {
+        return;
+    }
+
+    static constexpr ftl::Flags<RequestedLayerState::Changes> AFFECTS_INPUT =
+            RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Created |
+            RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
+            RequestedLayerState::Changes::Input;
+
+    if (args.forceUpdate != ForceUpdateFlags::ALL &&
+        !args.layerLifecycleManager.getGlobalChanges().any(AFFECTS_INPUT)) {
+        return;
+    }
+
+    for (auto& path : mNeedsTouchableRegionCrop) {
+        frontend::LayerSnapshot* snapshot = getSnapshot(path);
+        if (!snapshot) {
+            continue;
+        }
+        const std::optional<frontend::DisplayInfo> displayInfoOpt =
+                args.displays.get(snapshot->outputFilter.layerStack);
+        static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
+        auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
+
+        bool needsUpdate =
+                args.forceUpdate == ForceUpdateFlags::ALL || snapshot->changes.any(AFFECTS_INPUT);
+        auto cropLayerSnapshot = getSnapshot(snapshot->touchCropId);
+        needsUpdate =
+                needsUpdate || (cropLayerSnapshot && cropLayerSnapshot->changes.any(AFFECTS_INPUT));
+        auto clonedRootSnapshot = path.isClone() ? getSnapshot(snapshot->mirrorRootPath) : nullptr;
+        needsUpdate = needsUpdate ||
+                (clonedRootSnapshot && clonedRootSnapshot->changes.any(AFFECTS_INPUT));
+
+        if (!needsUpdate) {
+            continue;
+        }
+
+        if (snapshot->inputInfo.replaceTouchableRegionWithCrop) {
+            Rect inputBoundsInDisplaySpace;
+            if (!cropLayerSnapshot) {
+                FloatRect inputBounds = getInputBounds(*snapshot, /*fillParentBounds=*/true).first;
+                inputBoundsInDisplaySpace =
+                        getInputBoundsInDisplaySpace(*snapshot, inputBounds, displayInfo.transform);
+            } else {
+                FloatRect inputBounds =
+                        getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+                inputBoundsInDisplaySpace =
+                        getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+                                                     displayInfo.transform);
+            }
+            snapshot->inputInfo.touchableRegion = Region(inputBoundsInDisplaySpace);
+        } else if (cropLayerSnapshot) {
+            FloatRect inputBounds =
+                    getInputBounds(*cropLayerSnapshot, /*fillParentBounds=*/true).first;
+            Rect inputBoundsInDisplaySpace =
+                    getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
+                                                 displayInfo.transform);
+            snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(
+                    displayInfo.transform.transform(inputBoundsInDisplaySpace));
+        }
+
+        // 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.
+        if (clonedRootSnapshot) {
+            const Rect rect =
+                    displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds});
+            snapshot->inputInfo.touchableRegion =
+                    snapshot->inputInfo.touchableRegion.intersect(rect);
+        }
+    }
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
new file mode 100644
index 0000000..148c98e
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -0,0 +1,136 @@
+/*
+ * 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 "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "LayerHierarchy.h"
+#include "LayerSnapshot.h"
+#include "RequestedLayerState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Walks through the layer hierarchy to build an ordered list
+// of LayerSnapshots that can be passed on to CompositionEngine.
+// This builder does a minimum amount of work to update
+// an existing set of snapshots based on hierarchy changes
+// and RequestedLayerState changes.
+
+// The builder also uses a fast path to update
+// snapshots when there are only buffer updates.
+class LayerSnapshotBuilder {
+public:
+    enum class ForceUpdateFlags {
+        NONE,
+        ALL,
+        HIERARCHY,
+    };
+    struct Args {
+        LayerHierarchy root;
+        const LayerLifecycleManager& layerLifecycleManager;
+        ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE;
+        bool includeMetadata = false;
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays;
+        // Set to true if there were display changes since last update.
+        bool displayChanges = false;
+        const renderengine::ShadowSettings& globalShadowSettings;
+        bool supportsBlur = true;
+        bool forceFullDamage = false;
+        std::optional<FloatRect> parentCrop = std::nullopt;
+        std::unordered_set<uint32_t> excludeLayerIds;
+        const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata;
+        const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap;
+    };
+    LayerSnapshotBuilder();
+
+    // Rebuild the snapshots from scratch.
+    LayerSnapshotBuilder(Args);
+
+    // Update an existing set of snapshot using change flags in RequestedLayerState
+    // and LayerLifecycleManager. This needs to be called before
+    // LayerLifecycleManager.commitChanges is called as that function will clear all
+    // change flags.
+    void update(const Args&);
+    std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots();
+    LayerSnapshot* getSnapshot(uint32_t layerId) const;
+    LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;
+
+    typedef std::function<void(const LayerSnapshot& snapshot)> ConstVisitor;
+
+    // Visit each visible snapshot in z-order
+    void forEachVisibleSnapshot(const ConstVisitor& visitor) const;
+
+    // Visit each visible snapshot in z-order
+    void forEachVisibleSnapshot(const ConstVisitor& visitor, const LayerHierarchy& root) const;
+
+    typedef std::function<void(std::unique_ptr<LayerSnapshot>& snapshot)> Visitor;
+    // Visit each visible snapshot in z-order and move the snapshot if needed
+    void forEachVisibleSnapshot(const Visitor& visitor);
+
+    // Visit each snapshot interesting to input reverse z-order
+    void forEachInputSnapshot(const ConstVisitor& visitor) const;
+
+private:
+    friend class LayerSnapshotTest;
+    static LayerSnapshot getRootSnapshot();
+
+    // return true if we were able to successfully update the snapshots via
+    // the fast path.
+    bool tryFastUpdate(const Args& args);
+
+    void updateSnapshots(const Args& args);
+
+    const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
+                                                    LayerHierarchy::TraversalPath& traversalPath,
+                                                    const LayerSnapshot& parentSnapshot);
+    void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
+                        const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&);
+    static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
+                                    bool parentIsRelative, const Args& args);
+    static void resetRelativeState(LayerSnapshot& snapshot);
+    static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
+                                    const LayerSnapshot& parentSnapshot);
+    void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
+                           const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
+    static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+                              const renderengine::ShadowSettings& globalShadowSettings);
+    void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
+                     const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
+                     const Args& args);
+    // Return true if there are unreachable snapshots
+    bool sortSnapshotsByZ(const Args& args);
+    LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
+                                  const RequestedLayerState& layer,
+                                  const LayerSnapshot& parentSnapshot);
+    void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+                          const Args& args);
+    void updateTouchableRegionCrop(const Args& args);
+
+    std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
+                       LayerHierarchy::TraversalPathHash>
+            mIdToSnapshot;
+    // Track snapshots that needs touchable region crop from other snapshots
+    std::unordered_set<LayerHierarchy::TraversalPath, LayerHierarchy::TraversalPathHash>
+            mNeedsTouchableRegionCrop;
+    std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
+    LayerSnapshot mRootSnapshot;
+    bool mResortSnapshots = false;
+    int mNumInterestingSnapshots = 0;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
new file mode 100644
index 0000000..23bb54c
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -0,0 +1,483 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "RequestedLayerState"
+
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <sys/types.h>
+
+#include "Layer.h"
+#include "LayerCreationArgs.h"
+#include "LayerLog.h"
+#include "RequestedLayerState.h"
+
+namespace android::surfaceflinger::frontend {
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
+namespace {
+std::string layerIdsToString(const std::vector<uint32_t>& layerIds) {
+    std::stringstream stream;
+    stream << "{";
+    for (auto layerId : layerIds) {
+        stream << layerId << ",";
+    }
+    stream << "}";
+    return stream.str();
+}
+
+} // namespace
+
+RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
+      : id(args.sequence),
+        name(args.name + "#" + std::to_string(args.sequence)),
+        canBeRoot(args.addToRoot),
+        layerCreationFlags(args.flags),
+        textureName(args.textureName),
+        ownerUid(args.ownerUid),
+        ownerPid(args.ownerPid),
+        parentId(args.parentId),
+        layerIdToMirror(args.layerIdToMirror) {
+    layerId = static_cast<int32_t>(args.sequence);
+    changes |= RequestedLayerState::Changes::Created;
+    metadata.merge(args.metadata);
+    changes |= RequestedLayerState::Changes::Metadata;
+    handleAlive = true;
+    if (parentId != UNASSIGNED_LAYER_ID) {
+        canBeRoot = false;
+    }
+    if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
+        changes |= RequestedLayerState::Changes::Mirror;
+    } else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+        layerStackToMirror = args.layerStackToMirror;
+        changes |= RequestedLayerState::Changes::Mirror;
+    }
+
+    flags = 0;
+    if (args.flags & ISurfaceComposerClient::eHidden) flags |= layer_state_t::eLayerHidden;
+    if (args.flags & ISurfaceComposerClient::eOpaque) flags |= layer_state_t::eLayerOpaque;
+    if (args.flags & ISurfaceComposerClient::eSecure) flags |= layer_state_t::eLayerSecure;
+    if (args.flags & ISurfaceComposerClient::eSkipScreenshot) {
+        flags |= layer_state_t::eLayerSkipScreenshot;
+    }
+    premultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+    potentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+    protectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+    if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+        // Set an invalid color so there is no color fill.
+        // (b/259981098) use an explicit flag instead of relying on invalid values.
+        color.r = -1.0_hf;
+        color.g = -1.0_hf;
+        color.b = -1.0_hf;
+    } else {
+        color.rgb = {0.0_hf, 0.0_hf, 0.0_hf};
+    }
+    LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
+    color.a = 1.0f;
+
+    crop.makeInvalid();
+    z = 0;
+    layerStack = ui::DEFAULT_LAYER_STACK;
+    transformToDisplayInverse = false;
+    dataspace = ui::Dataspace::UNKNOWN;
+    desiredHdrSdrRatio = 1.f;
+    currentHdrSdrRatio = 1.f;
+    dataspaceRequested = false;
+    hdrMetadata.validTypes = 0;
+    surfaceDamageRegion = Region::INVALID_REGION;
+    cornerRadius = 0.0f;
+    backgroundBlurRadius = 0;
+    api = -1;
+    hasColorTransform = false;
+    bufferTransform = 0;
+    requestedTransform.reset();
+    bufferData = std::make_shared<BufferData>();
+    bufferData->frameNumber = 0;
+    bufferData->acquireFence = sp<Fence>::make(-1);
+    acquireFenceTime = std::make_shared<FenceTime>(bufferData->acquireFence);
+    colorSpaceAgnostic = false;
+    frameRateSelectionPriority = Layer::PRIORITY_UNSET;
+    shadowRadius = 0.f;
+    fixedTransformHint = ui::Transform::ROT_INVALID;
+    destinationFrame.makeInvalid();
+    isTrustedOverlay = false;
+    dropInputMode = gui::DropInputMode::NONE;
+    dimmingEnabled = true;
+    defaultFrameRateCompatibility =
+            static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+    dataspace = ui::Dataspace::V0_SRGB;
+    gameMode = gui::GameMode::Unsupported;
+    requestedFrameRate = {};
+    cachingHint = gui::CachingHint::Enabled;
+}
+
+void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
+    const uint32_t oldFlags = flags;
+    const half oldAlpha = color.a;
+    const bool hadBuffer = externalTexture != nullptr;
+    const bool hadSideStream = sidebandStream != nullptr;
+    const layer_state_t& clientState = resolvedComposerState.state;
+    const bool hadBlur = hasBlur();
+    uint64_t clientChanges = what | layer_state_t::diff(clientState);
+    layer_state_t::merge(clientState);
+    what = clientChanges;
+
+    if (clientState.what & layer_state_t::eFlagsChanged) {
+        if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
+        }
+        if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) {
+            changes |= RequestedLayerState::Changes::Geometry;
+        }
+    }
+    if (clientState.what & layer_state_t::eBufferChanged) {
+        externalTexture = resolvedComposerState.externalTexture;
+        barrierProducerId = std::max(bufferData->producerId, barrierProducerId);
+        barrierFrameNumber = std::max(bufferData->frameNumber, barrierFrameNumber);
+        // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
+
+        const bool hasBuffer = externalTexture != nullptr;
+        if (hasBuffer || hasBuffer != hadBuffer) {
+            changes |= RequestedLayerState::Changes::Buffer;
+        }
+
+        if (hasBuffer != hadBuffer) {
+            changes |= RequestedLayerState::Changes::Geometry |
+                    RequestedLayerState::Changes::VisibleRegion |
+                    RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+        }
+    }
+
+    if (clientState.what & layer_state_t::eSidebandStreamChanged) {
+        changes |= RequestedLayerState::Changes::SidebandStream;
+        const bool hasSideStream = sidebandStream != nullptr;
+        if (hasSideStream != hadSideStream) {
+            changes |= RequestedLayerState::Changes::Geometry |
+                    RequestedLayerState::Changes::VisibleRegion |
+                    RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input;
+        }
+    }
+    if (what & (layer_state_t::eAlphaChanged)) {
+        if (oldAlpha == 0 || color.a == 0) {
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
+        }
+    }
+    if (what & (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged)) {
+        if (hadBlur != hasBlur()) {
+            changes |= RequestedLayerState::Changes::Visibility |
+                    RequestedLayerState::Changes::VisibleRegion;
+        }
+    }
+    if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
+        changes |= RequestedLayerState::Changes::Hierarchy;
+    if (clientChanges & layer_state_t::CONTENT_CHANGES)
+        changes |= RequestedLayerState::Changes::Content;
+    if (clientChanges & layer_state_t::GEOMETRY_CHANGES)
+        changes |= RequestedLayerState::Changes::Geometry;
+    if (clientChanges & layer_state_t::AFFECTS_CHILDREN)
+        changes |= RequestedLayerState::Changes::AffectsChildren;
+    if (clientChanges & layer_state_t::INPUT_CHANGES)
+        changes |= RequestedLayerState::Changes::Input;
+    if (clientChanges & layer_state_t::VISIBLE_REGION_CHANGES)
+        changes |= RequestedLayerState::Changes::VisibleRegion;
+    if (clientState.what & layer_state_t::eColorTransformChanged) {
+        static const mat4 identityMatrix = mat4();
+        hasColorTransform = colorTransform != identityMatrix;
+    }
+    if (clientState.what &
+        (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
+         layer_state_t::eLayerStackChanged)) {
+        changes |= RequestedLayerState::Changes::Z;
+    }
+    if (clientState.what & layer_state_t::eReparent) {
+        changes |= RequestedLayerState::Changes::Parent;
+        parentId = resolvedComposerState.parentId;
+        parentSurfaceControlForChild = nullptr;
+        // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+        // but thats the existing logic and until we make this behavior more explicit, we need
+        // to maintain this logic.
+        canBeRoot = false;
+    }
+    if (clientState.what & layer_state_t::eRelativeLayerChanged) {
+        changes |= RequestedLayerState::Changes::RelativeParent;
+        relativeParentId = resolvedComposerState.relativeParentId;
+        isRelativeOf = true;
+        relativeLayerSurfaceControl = nullptr;
+    }
+    if ((clientState.what & layer_state_t::eLayerChanged ||
+         (clientState.what & layer_state_t::eReparent && parentId == UNASSIGNED_LAYER_ID)) &&
+        isRelativeOf) {
+        // clear out relz data
+        relativeParentId = UNASSIGNED_LAYER_ID;
+        isRelativeOf = false;
+        changes |= RequestedLayerState::Changes::RelativeParent;
+    }
+    if (clientState.what & layer_state_t::eReparent && parentId == relativeParentId) {
+        // provide a hint that we are are now a direct child and not a relative child.
+        changes |= RequestedLayerState::Changes::RelativeParent;
+    }
+    if (clientState.what & layer_state_t::eInputInfoChanged) {
+        touchCropId = resolvedComposerState.touchCropId;
+        windowInfoHandle->editInfo()->touchableRegionCropHandle.clear();
+    }
+    if (clientState.what & layer_state_t::eStretchChanged) {
+        stretchEffect.sanitize();
+    }
+
+    if (clientState.what & layer_state_t::eHasListenerCallbacksChanged) {
+        // TODO(b/238781169) handle callbacks
+    }
+
+    if (clientState.what & layer_state_t::ePositionChanged) {
+        requestedTransform.set(x, y);
+    }
+
+    if (clientState.what & layer_state_t::eMatrixChanged) {
+        requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+    }
+    if (clientState.what & layer_state_t::eMetadataChanged) {
+        const int32_t requestedGameMode =
+                clientState.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
+        if (requestedGameMode != -1) {
+            // The transaction will be received on the Task layer and needs to be applied to all
+            // child layers.
+            if (static_cast<int32_t>(gameMode) != requestedGameMode) {
+                gameMode = static_cast<gui::GameMode>(requestedGameMode);
+                changes |= RequestedLayerState::Changes::AffectsChildren;
+            }
+        }
+    }
+    if (clientState.what & layer_state_t::eFrameRateChanged) {
+        const auto compatibility =
+                Layer::FrameRate::convertCompatibility(clientState.frameRateCompatibility);
+        const auto strategy = Layer::FrameRate::convertChangeFrameRateStrategy(
+                clientState.changeFrameRateStrategy);
+        requestedFrameRate =
+                Layer::FrameRate(Fps::fromValue(clientState.frameRate), compatibility, strategy);
+        changes |= RequestedLayerState::Changes::FrameRate;
+    }
+}
+
+ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
+    uint32_t bufferWidth = externalTexture->getWidth();
+    uint32_t bufferHeight = externalTexture->getHeight();
+    // Undo any transformations on the buffer.
+    if (bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+    if (transformToDisplayInverse) {
+        if (displayRotationFlags & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+    return {bufferWidth, bufferHeight};
+}
+
+ui::Transform RequestedLayerState::getTransform(uint32_t displayRotationFlags) const {
+    if ((flags & layer_state_t::eIgnoreDestinationFrame) || destinationFrame.isEmpty()) {
+        // If destination frame is not set, use the requested transform set via
+        // Transaction::setPosition and Transaction::setMatrix.
+        return requestedTransform;
+    }
+
+    Rect destRect = destinationFrame;
+    int32_t destW = destRect.width();
+    int32_t destH = destRect.height();
+    if (destRect.left < 0) {
+        destRect.left = 0;
+        destRect.right = destW;
+    }
+    if (destRect.top < 0) {
+        destRect.top = 0;
+        destRect.bottom = destH;
+    }
+
+    if (!externalTexture) {
+        ui::Transform transform;
+        transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+        return transform;
+    }
+
+    ui::Size bufferSize = getUnrotatedBufferSize(displayRotationFlags);
+
+    float sx = static_cast<float>(destW) / static_cast<float>(bufferSize.width);
+    float sy = static_cast<float>(destH) / static_cast<float>(bufferSize.height);
+    ui::Transform transform;
+    transform.set(sx, 0, 0, sy);
+    transform.set(static_cast<float>(destRect.left), static_cast<float>(destRect.top));
+    return transform;
+}
+
+std::string RequestedLayerState::getDebugString() const {
+    std::stringstream debug;
+    debug << "RequestedLayerState{" << name;
+    if (parentId != UNASSIGNED_LAYER_ID) debug << " parentId=" << parentId;
+    if (relativeParentId != UNASSIGNED_LAYER_ID) debug << " relativeParentId=" << relativeParentId;
+    if (!mirrorIds.empty()) debug << " mirrorId=" << layerIdsToString(mirrorIds);
+    if (!handleAlive) debug << " !handle";
+    if (z != 0) debug << " z=" << z;
+    if (layerStack.id != 0) debug << " layerStack=" << layerStack.id;
+    return debug.str();
+}
+
+std::string RequestedLayerState::getDebugStringShort() const {
+    return "[" + std::to_string(id) + "]" + name;
+}
+
+bool RequestedLayerState::canBeDestroyed() const {
+    return !handleAlive && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isRoot() const {
+    return canBeRoot && parentId == UNASSIGNED_LAYER_ID;
+}
+bool RequestedLayerState::isHiddenByPolicy() const {
+    return (flags & layer_state_t::eLayerHidden) == layer_state_t::eLayerHidden;
+};
+half4 RequestedLayerState::getColor() const {
+    if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+        return {0._hf, 0._hf, 0._hf, color.a};
+    }
+    return color;
+}
+Rect RequestedLayerState::getBufferSize(uint32_t displayRotationFlags) const {
+    // for buffer state layers we use the display frame size as the buffer size.
+    if (!externalTexture) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = externalTexture->getWidth();
+    uint32_t bufHeight = externalTexture->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (transformToDisplayInverse) {
+        uint32_t invTransform = displayRotationFlags;
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
+    Rect size = bufferSize;
+    if (!crop.isEmpty() && size.isValid()) {
+        size.intersect(crop, &size);
+    } else if (!crop.isEmpty()) {
+        size = crop;
+    }
+    return size;
+}
+
+Rect RequestedLayerState::getBufferCrop() const {
+    // this is the crop rectangle that applies to the buffer
+    // itself (as opposed to the window)
+    if (!bufferCrop.isEmpty()) {
+        // if the buffer crop is defined, we use that
+        return bufferCrop;
+    } else if (externalTexture != nullptr) {
+        // otherwise we use the whole buffer
+        return externalTexture->getBounds();
+    } else {
+        // if we don't have a buffer yet, we use an empty/invalid crop
+        return Rect();
+    }
+}
+
+aidl::android::hardware::graphics::composer3::Composition RequestedLayerState::getCompositionType()
+        const {
+    using aidl::android::hardware::graphics::composer3::Composition;
+    // TODO(b/238781169) check about sidestream ready flag
+    if (sidebandStream.get()) {
+        return Composition::SIDEBAND;
+    }
+    if (!externalTexture) {
+        return Composition::SOLID_COLOR;
+    }
+    if (flags & layer_state_t::eLayerIsDisplayDecoration) {
+        return Composition::DISPLAY_DECORATION;
+    }
+    if (potentialCursor) {
+        return Composition::CURSOR;
+    }
+    return Composition::DEVICE;
+}
+
+Rect RequestedLayerState::reduce(const Rect& win, const Region& exclude) {
+    if (CC_LIKELY(exclude.isEmpty())) {
+        return win;
+    }
+    if (exclude.isRect()) {
+        return win.reduce(exclude.getBounds());
+    }
+    return Region(win).subtract(exclude).getBounds();
+}
+
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+    return isRelativeOf &&
+            (parentId != relativeParentId || relativeParentId == UNASSIGNED_LAYER_ID);
+}
+
+bool RequestedLayerState::hasInputInfo() const {
+    if (!windowInfoHandle) {
+        return false;
+    }
+    const auto windowInfo = windowInfoHandle->getInfo();
+    return windowInfo->token != nullptr ||
+            windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
+bool RequestedLayerState::hasBlur() const {
+    return backgroundBlurRadius > 0 || blurRegions.size() > 0;
+}
+
+bool RequestedLayerState::hasFrameUpdate() const {
+    return what & layer_state_t::CONTENT_DIRTY &&
+            (externalTexture || bgColorLayerId != UNASSIGNED_LAYER_ID);
+}
+
+bool RequestedLayerState::hasReadyFrame() const {
+    return hasFrameUpdate() || changes.test(Changes::SidebandStream) || autoRefresh;
+}
+
+bool RequestedLayerState::hasSidebandStreamFrame() const {
+    return hasFrameUpdate() && sidebandStream.get();
+}
+
+bool RequestedLayerState::willReleaseBufferOnLatch() const {
+    return changes.test(Changes::Buffer) && !externalTexture;
+}
+
+void RequestedLayerState::clearChanges() {
+    what = 0;
+    changes.clear();
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
new file mode 100644
index 0000000..0ef50bc
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -0,0 +1,126 @@
+/*
+ * 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 <aidl/android/hardware/graphics/composer3/Composition.h>
+#include <ftl/flags.h>
+#include <gui/LayerState.h>
+#include <renderengine/ExternalTexture.h>
+#include "Scheduler/LayerInfo.h"
+
+#include "LayerCreationArgs.h"
+#include "TransactionState.h"
+
+namespace android::surfaceflinger::frontend {
+
+// Stores client requested states for a layer.
+// This struct does not store any other states or states pertaining to
+// other layers. Links to other layers that are part of the client
+// requested state such as parent are translated to layer id so
+// we can avoid extending the lifetime of layer handles.
+struct RequestedLayerState : layer_state_t {
+    // Changes in state after merging with new state. This includes additional state
+    // changes found in layer_state_t::what.
+    enum class Changes : uint32_t {
+        Created = 1u << 0,
+        Destroyed = 1u << 1,
+        Hierarchy = 1u << 2,
+        Geometry = 1u << 3,
+        Content = 1u << 4,
+        Input = 1u << 5,
+        Z = 1u << 6,
+        Mirror = 1u << 7,
+        Parent = 1u << 8,
+        RelativeParent = 1u << 9,
+        Metadata = 1u << 10,
+        Visibility = 1u << 11,
+        AffectsChildren = 1u << 12,
+        FrameRate = 1u << 13,
+        VisibleRegion = 1u << 14,
+        Buffer = 1u << 15,
+        SidebandStream = 1u << 16,
+        Animation = 1u << 17,
+    };
+    static Rect reduce(const Rect& win, const Region& exclude);
+    RequestedLayerState(const LayerCreationArgs&);
+    void merge(const ResolvedComposerState&);
+    void clearChanges();
+
+    // Currently we only care about the primary display
+    ui::Transform getTransform(uint32_t displayRotationFlags) const;
+    ui::Size getUnrotatedBufferSize(uint32_t displayRotationFlags) const;
+    bool canBeDestroyed() const;
+    bool isRoot() const;
+    bool isHiddenByPolicy() const;
+    half4 getColor() const;
+    Rect getBufferSize(uint32_t displayRotationFlags) const;
+    Rect getCroppedBufferSize(const Rect& bufferSize) const;
+    Rect getBufferCrop() const;
+    std::string getDebugString() const;
+    std::string getDebugStringShort() const;
+    aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+    bool hasValidRelativeParent() const;
+    bool hasInputInfo() const;
+    bool hasBlur() const;
+    bool hasFrameUpdate() const;
+    bool hasReadyFrame() const;
+    bool hasSidebandStreamFrame() const;
+    bool willReleaseBufferOnLatch() const;
+
+    // Layer serial number.  This gives layers an explicit ordering, so we
+    // have a stable sort order when their layer stack and Z-order are
+    // the same.
+    const uint32_t id;
+    const std::string name;
+    bool canBeRoot = false;
+    const uint32_t layerCreationFlags;
+    const uint32_t textureName;
+    // The owner of the layer. If created from a non system process, it will be the calling uid.
+    // If created from a system process, the value can be passed in.
+    const uid_t ownerUid;
+    // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+    // If created from a system process, the value can be passed in.
+    const pid_t ownerPid;
+    bool dataspaceRequested;
+    bool hasColorTransform;
+    bool premultipliedAlpha{true};
+    // This layer can be a cursor on some displays.
+    bool potentialCursor{false};
+    bool protectedByApp{false}; // application requires protected path to external sink
+    ui::Transform requestedTransform;
+    std::shared_ptr<FenceTime> acquireFenceTime;
+    std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+    gui::GameMode gameMode;
+    scheduler::LayerInfo::FrameRate requestedFrameRate;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+    uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+    ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+    uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+    uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
+    uint64_t barrierFrameNumber = 0;
+    uint32_t barrierProducerId = 0;
+
+    // book keeping states
+    bool handleAlive = true;
+    bool isRelativeOf = false;
+    std::vector<uint32_t> mirrorIds{};
+    ftl::Flags<RequestedLayerState::Changes> changes;
+    bool bgColorLayer = false;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h
new file mode 100644
index 0000000..0061c53
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/SwapErase.h
@@ -0,0 +1,48 @@
+/*
+ * 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 <vector>
+
+namespace android::surfaceflinger::frontend {
+// Erases the first element in vec that matches value. This is a more optimal way to
+// remove an element from a vector that avoids relocating all the elements after the one
+// that is erased.
+template <typename T>
+bool swapErase(std::vector<T>& vec, const T& value) {
+    bool found = false;
+    auto it = std::find(vec.begin(), vec.end(), value);
+    if (it != vec.end()) {
+        std::iter_swap(it, vec.end() - 1);
+        vec.erase(vec.end() - 1);
+        found = true;
+    }
+    return found;
+}
+
+// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element
+// that returns true for predicate.
+template <typename T, class P>
+void swapErase(std::vector<T>& vec, P predicate) {
+    auto it = std::find_if(vec.begin(), vec.end(), predicate);
+    if (it != vec.end()) {
+        std::iter_swap(it, vec.end() - 1);
+        vec.erase(vec.end() - 1);
+    }
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
new file mode 100644
index 0000000..9cbe0bb
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "TransactionHandler"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/trace.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "TransactionHandler.h"
+
+namespace android::surfaceflinger::frontend {
+
+void TransactionHandler::queueTransaction(TransactionState&& state) {
+    mLocklessTransactionQueue.push(std::move(state));
+    mPendingTransactionCount.fetch_add(1);
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+}
+
+std::vector<TransactionState> TransactionHandler::flushTransactions() {
+    while (!mLocklessTransactionQueue.isEmpty()) {
+        auto maybeTransaction = mLocklessTransactionQueue.pop();
+        if (!maybeTransaction.has_value()) {
+            break;
+        }
+        auto transaction = maybeTransaction.value();
+        mPendingTransactionQueues[transaction.applyToken].emplace(std::move(transaction));
+    }
+
+    // Collect transaction that are ready to be applied.
+    std::vector<TransactionState> transactions;
+    TransactionFlushState flushState;
+    flushState.queueProcessTime = systemTime();
+    // Transactions with a buffer pending on a barrier may be on a different applyToken
+    // than the transaction which satisfies our barrier. In fact this is the exact use case
+    // that the primitive is designed for. This means we may first process
+    // the barrier dependent transaction, determine it ineligible to complete
+    // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
+    // The barrier dependent transaction was eligible to be presented in this frame
+    // but we would have prevented it without case. To fix this we continually
+    // loop through flushPendingTransactionQueues until we perform an iteration
+    // where the number of transactionsPendingBarrier doesn't change. This way
+    // we can continue to resolve dependency chains of barriers as far as possible.
+    int lastTransactionsPendingBarrier = 0;
+    int transactionsPendingBarrier = 0;
+    do {
+        lastTransactionsPendingBarrier = transactionsPendingBarrier;
+        // Collect transactions that are ready to be applied.
+        transactionsPendingBarrier = flushPendingTransactionQueues(transactions, flushState);
+    } while (lastTransactionsPendingBarrier != transactionsPendingBarrier);
+
+    applyUnsignaledBufferTransaction(transactions, flushState);
+
+    mPendingTransactionCount.fetch_sub(transactions.size());
+    ATRACE_INT("TransactionQueue", static_cast<int>(mPendingTransactionCount.load()));
+    return transactions;
+}
+
+void TransactionHandler::applyUnsignaledBufferTransaction(
+        std::vector<TransactionState>& transactions, TransactionFlushState& flushState) {
+    if (!flushState.queueWithUnsignaledBuffer) {
+        return;
+    }
+
+    // only apply an unsignaled buffer transaction if it's the first one
+    if (!transactions.empty()) {
+        ATRACE_NAME("fence unsignaled");
+        return;
+    }
+
+    auto it = mPendingTransactionQueues.find(flushState.queueWithUnsignaledBuffer);
+    LOG_ALWAYS_FATAL_IF(it == mPendingTransactionQueues.end(),
+                        "Could not find queue with unsignaled buffer!");
+
+    auto& queue = it->second;
+    popTransactionFromPending(transactions, flushState, queue);
+    if (queue.empty()) {
+        it = mPendingTransactionQueues.erase(it);
+    }
+}
+
+void TransactionHandler::popTransactionFromPending(std::vector<TransactionState>& transactions,
+                                                   TransactionFlushState& flushState,
+                                                   std::queue<TransactionState>& queue) {
+    auto& transaction = queue.front();
+    // Transaction is ready move it from the pending queue.
+    flushState.firstTransaction = false;
+    removeFromStalledTransactions(transaction.id);
+    transactions.emplace_back(std::move(transaction));
+    queue.pop();
+
+    auto& readyToApplyTransaction = transactions.back();
+    readyToApplyTransaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
+        const bool frameNumberChanged =
+                state.bufferData->flags.test(BufferData::BufferDataChange::frameNumberChanged);
+        if (frameNumberChanged) {
+            flushState.bufferLayersReadyToPresent.emplace_or_replace(state.surface.get(),
+                                                                     state.bufferData->frameNumber);
+        } else {
+            // Barrier function only used for BBQ which always includes a frame number.
+            // This value only used for barrier logic.
+            flushState.bufferLayersReadyToPresent
+                    .emplace_or_replace(state.surface.get(), std::numeric_limits<uint64_t>::max());
+        }
+    });
+}
+
+TransactionHandler::TransactionReadiness TransactionHandler::applyFilters(
+        TransactionFlushState& flushState) {
+    auto ready = TransactionReadiness::Ready;
+    for (auto& filter : mTransactionReadyFilters) {
+        auto perFilterReady = filter(flushState);
+        switch (perFilterReady) {
+            case TransactionReadiness::NotReady:
+            case TransactionReadiness::NotReadyBarrier:
+                return perFilterReady;
+
+            case TransactionReadiness::NotReadyUnsignaled:
+                // If one of the filters allows latching an unsignaled buffer, latch this ready
+                // state.
+                ready = perFilterReady;
+                break;
+            case TransactionReadiness::Ready:
+                continue;
+        }
+    }
+    return ready;
+}
+
+int TransactionHandler::flushPendingTransactionQueues(std::vector<TransactionState>& transactions,
+                                                      TransactionFlushState& flushState) {
+    int transactionsPendingBarrier = 0;
+    auto it = mPendingTransactionQueues.begin();
+    while (it != mPendingTransactionQueues.end()) {
+        auto& [applyToken, queue] = *it;
+        while (!queue.empty()) {
+            auto& transaction = queue.front();
+            flushState.transaction = &transaction;
+            auto ready = applyFilters(flushState);
+            if (ready == TransactionReadiness::NotReadyBarrier) {
+                transactionsPendingBarrier++;
+                break;
+            } else if (ready == TransactionReadiness::NotReady) {
+                break;
+            } else if (ready == TransactionReadiness::NotReadyUnsignaled) {
+                // We maybe able to latch this transaction if it's the only transaction
+                // ready to be applied.
+                flushState.queueWithUnsignaledBuffer = applyToken;
+                break;
+            }
+            // ready == TransactionReadiness::Ready
+            popTransactionFromPending(transactions, flushState, queue);
+        }
+
+        if (queue.empty()) {
+            it = mPendingTransactionQueues.erase(it);
+        } else {
+            it = std::next(it, 1);
+        }
+    }
+    return transactionsPendingBarrier;
+}
+
+void TransactionHandler::addTransactionReadyFilter(TransactionFilter&& filter) {
+    mTransactionReadyFilters.emplace_back(std::move(filter));
+}
+
+bool TransactionHandler::hasPendingTransactions() {
+    return !mPendingTransactionQueues.empty() || !mLocklessTransactionQueue.isEmpty();
+}
+
+void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
+                                                   sp<ITransactionCompletedListener>& listener,
+                                                   const std::string& reason) {
+    if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
+        mStalledTransactions.end()) {
+        return;
+    }
+
+    mStalledTransactions.push_back(transactionId);
+    listener->onTransactionQueueStalled(String8(reason.c_str()));
+}
+
+void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
+    auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
+    if (it != mStalledTransactions.end()) {
+        mStalledTransactions.erase(it);
+    }
+}
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
new file mode 100644
index 0000000..865835f
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -0,0 +1,85 @@
+/*
+ * 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 <semaphore.h>
+#include <cstdint>
+#include <vector>
+
+#include <LocklessQueue.h>
+#include <TransactionState.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
+#include <ftl/small_vector.h>
+
+namespace android {
+
+class TestableSurfaceFlinger;
+namespace surfaceflinger::frontend {
+
+class TransactionHandler {
+public:
+    struct TransactionFlushState {
+        TransactionState* transaction;
+        bool firstTransaction = true;
+        nsecs_t queueProcessTime = 0;
+        // Layer handles that have transactions with buffers that are ready to be applied.
+        ftl::SmallMap<IBinder* /* binder address */, uint64_t /* framenumber */, 15>
+                bufferLayersReadyToPresent = {};
+        // Tracks the queue with an unsignaled buffer. This is used to handle
+        // LatchUnsignaledConfig::AutoSingleLayer to ensure we only apply an unsignaled buffer
+        // if it's the only transaction that is ready to be applied.
+        sp<IBinder> queueWithUnsignaledBuffer = nullptr;
+    };
+    enum class TransactionReadiness {
+        // Transaction is ready to be applied
+        Ready,
+        // Transaction has unmet conditions (fence, present time, etc) and cannot be applied.
+        NotReady,
+        // Transaction is waiting on a barrier (another buffer to be latched first)
+        NotReadyBarrier,
+        // Transaction has an unsignaled fence but can be applied if it's the only transaction
+        NotReadyUnsignaled,
+    };
+    using TransactionFilter = std::function<TransactionReadiness(const TransactionFlushState&)>;
+
+    bool hasPendingTransactions();
+    std::vector<TransactionState> flushTransactions();
+    void addTransactionReadyFilter(TransactionFilter&&);
+    void queueTransaction(TransactionState&&);
+    void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
+                                   const std::string& reason);
+    void removeFromStalledTransactions(uint64_t transactionId);
+
+private:
+    // For unit tests
+    friend class ::android::TestableSurfaceFlinger;
+
+    int flushPendingTransactionQueues(std::vector<TransactionState>&, TransactionFlushState&);
+    void applyUnsignaledBufferTransaction(std::vector<TransactionState>&, TransactionFlushState&);
+    void popTransactionFromPending(std::vector<TransactionState>&, TransactionFlushState&,
+                                   std::queue<TransactionState>&);
+    TransactionReadiness applyFilters(TransactionFlushState&);
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
+            mPendingTransactionQueues;
+    LocklessQueue<TransactionState> mLocklessTransactionQueue;
+    std::atomic<size_t> mPendingTransactionCount = 0;
+    ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
+    std::vector<uint64_t> mStalledTransactions;
+};
+} // namespace surfaceflinger::frontend
+} // namespace android
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
new file mode 100644
index 0000000..e1449b6
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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 <gui/DisplayInfo.h>
+
+#include "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "TransactionState.h"
+
+namespace android {
+struct LayerCreatedState {
+    LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot)
+          : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
+    wp<Layer> layer;
+    // Indicates the initial parent of the created layer, only used for creating layer in
+    // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
+    wp<Layer> initialParent;
+    // Indicates whether the layer getting created should be added at root if there's no parent
+    // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
+    // be added offscreen.
+    bool addToRoot;
+};
+} // namespace android
+
+namespace android::surfaceflinger::frontend {
+
+// Atomic set of changes affecting layer state. These changes are queued in binder threads and
+// applied every vsync.
+struct Update {
+    std::vector<TransactionState> transactions;
+    std::vector<LayerCreatedState> layerCreatedStates;
+    std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
+    std::vector<LayerCreationArgs> layerCreationArgs;
+    std::vector<uint32_t> destroyedHandles;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md
new file mode 100644
index 0000000..e5f51a5
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/readme.md
@@ -0,0 +1,110 @@
+# SurfaceFlinger FrontEnd
+
+SurfaceFlinger FrontEnd implements the client APIs that describe how buffers should be
+composited on the screen. Layers are used to capture how the buffer should be composited
+and each buffer is associated with a Layer. Transactions contain an atomic set of changes
+to one or more of these layers. The FrontEnd consumes these transactions, maintains the
+layer lifecycle, and provides a snapshot to the composition engine every frame that
+describes how a set of buffers should be composited.
+
+
+
+## Layers
+Layers are used to describe how a buffer should be placed on the display relative to other
+buffers. They are represented as a hierarchy, similar to a scene graph. Child layers can
+inherit some properties from their parents, which allows higher-level system components to
+maintain policies at different levels without needing to understand the entire hierarchy.
+This allows control to be delegated to different parts of the system - such as SystemServer,
+SysUI and Apps.
+
+### 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
+layer can also be kept alive if the layer has a parent, since the parent will hold a
+strong reference to the children. If the layer is not reachable but its handle is alive,
+the layer will be offscreen and its resources will not be freed. Clients must explicitly
+release all references to the handle as soon as it's done with the layer. It's strongly
+recommended to explicitly release the layer in Java and not rely on the GC.
+
+
+
+## Transactions
+Transactions contain a group of changes to one or more layers that are applied together.
+Transactions can be merged to apply a set of changes atomically. Merges are associative,
+meaning how you group the merges does not matter, but they are not commutative, meaning
+that the order in which you merge them does.
+For example:
+
+`Transaction a; a.setAlpha(sc, 2);`
+
+`Transaction b; b.setAlpha(sc, 4);`
+
+`a.merge(b)` is not the same as `b.merge(a)`
+
+<p>
+
+`Transaction c; c.setAlpha(sc, 6);`
+
+`a.merge(b).merge(c)` is the same as `b.merge(c); a.merge(b);`
+
+Transactions are queued in SurfaceFlinger per ApplyToken so order is only guaranteed for
+Transactions with the same applyToken. By default each process and each buffer producer
+provides a unique ApplyToken. This prevents clients from affecting one another, and possibly
+slowing each other down.
+
+
+
+## Architecture
+SurfaceFlinger FrontEnd intends to optimize for predictability and performance because state
+generation is on the hotpath. Simple buffer updates should be as fast as possible, and they
+should be consistently fast. This means avoiding contention (e.g., locks) and context
+switching. We also want to avoid doing anything that does not contribute to putting a pixel
+on the display.
+
+The pipeline can be broken down into five stages:
+- Queue and filter transactions that are ready to be committed.
+- Handle layer lifecycles and update server-side state per layer.
+- Generate and/or update the traversal trees.
+- Generate a z-ordered list of snapshots.
+- Emit callbacks back to clients
+
+
+### TransactionHandler
+TransactionHandler is responsible for queuing and filtering transactions that are ready to
+be applied. On commit, we filter the transactions that are ready. We provide an interface
+for other components to apply their own filter to determine if a transaction is ready to be
+applied.
+
+
+### LayerLifecycleManager
+RequestedLayerState is a simple data class that stores the server side layer state.
+Transactions are merged into this state, similar to how transactions can be merged on the
+client side. The states can always be reconstructed from LayerCreationArgs and a list of
+transactions. LayerLifecycleManager keeps track of Layer handle lifecycle and the layer
+lifecycle itself. It consumes a list of transactions and generates a list of server side
+states and change flags. Other components can register to listen to layer lifecycles.
+
+
+### LayerHierarchyBuilder
+LayerHierarchyBuilder consumes a list of RequestedLayerStates to generate a LayerHierarchy.
+The hierarchy provides functions for breadth-first traversal and z-order traversal of the
+entire tree or a subtree. Internally, the hierarchy is represented by a graph. Mirrored
+layers are represented by the same node in the graph with multiple parents. This allows us
+to implement mirroring without cloning Layers and maintaining complex hierarchies.
+
+
+### LayerSnapshotBuilder
+LayerSnapshotBuilder consumes a LayerHierarchy along with a list of RequestedLayerStates to
+generate a flattened z-ordered list of LayerSnapshots. LayerSnapshots contain all the data
+required for CompositionEngine and RenderEngine. It has no dependencies to FrontEnd, or the
+LayerHierarchy used to create them. They can be cloned and consumed freely. Other consumers
+like WindowInfo listeners (input and accessibility) also updated from these snapshots.
+
+Change flags are used to efficiently traverse this hierarchy where possible. This allows us
+to support short circuiting parts of the hierarchy, partial hierarchy updates and fast paths
+for buffer updates.
+
+
+While they can be cloned, the current implementation moves the snapshot from FrontEnd to
+CompositionEngine to avoid needless work in the hotpath. For snapshot consumers not critical
+to composition, the goal is to clone the snapshots and consume them on a background thread.
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index c06e300..9eefbe4 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -40,7 +40,8 @@
 
     for (const auto& listener : toInvoke) {
         ATRACE_NAME("invoking onHdrLayerInfoChanged");
-        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags);
+        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
+                                        info.maxDesiredHdrSdrRatio);
     }
 }
 
@@ -51,7 +52,7 @@
 
 void HdrLayerInfoReporter::addListener(const sp<gui::IHdrLayerInfoListener>& listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
-    asBinder->linkToDeath(this);
+    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
     std::lock_guard lock(mMutex);
     mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, HdrLayerInfo{}});
 }
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 4ada2b6..bf7c775 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -33,13 +33,29 @@
         int32_t maxW = 0;
         int32_t maxH = 0;
         int32_t flags = 0;
+        // Counter-intuitively a value of "1" means "as much as you can give me" due to "1" being
+        // the default value for all layers, so any HDR layer with a value of 1.f means no
+        // reduced maximum has been requested
+        // TODO: Should the max desired ratio have a better meaning for HLG/PQ so this can be
+        // eliminated? If we assume an SDR white point of even just 100 nits for those content
+        // then HLG could have a meaningful max ratio of 10.f and PQ of 100.f instead of needing
+        // to treat 1.f as "uncapped"
+        // With peak display brightnesses exceeding 1,000 nits currently, HLG's request could
+        // actually be satisfied in some ambient conditions such that limiting that max for that
+        // content in theory makes sense
+        float maxDesiredHdrSdrRatio = 0.f;
 
         bool operator==(const HdrLayerInfo& other) const {
             return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
-                    maxH == other.maxH && flags == other.flags;
+                    maxH == other.maxH && flags == other.flags &&
+                    maxDesiredHdrSdrRatio == other.maxDesiredHdrSdrRatio;
         }
 
         bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+
+        void mergeDesiredRatio(float update) {
+            maxDesiredHdrSdrRatio = std::max(maxDesiredHdrSdrRatio, update);
+        }
     };
 
     HdrLayerInfoReporter() = default;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a31cdf0..f12aab7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -29,6 +29,7 @@
 #include <android-base/stringprintf.h>
 #include <android/native_window.h>
 #include <binder/IPCThreadState.h>
+#include <compositionengine/CompositionEngine.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
@@ -41,6 +42,7 @@
 #include <gui/BufferItem.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
+#include <gui/TraceUtils.h>
 #include <math.h>
 #include <private/android_filesystem_config.h>
 #include <renderengine/RenderEngine.h>
@@ -50,8 +52,11 @@
 #include <system/graphics-base-v1.0.h>
 #include <ui/DataspaceUtils.h>
 #include <ui/DebugUtils.h>
+#include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <ui/Transform.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/NativeHandle.h>
@@ -60,75 +65,117 @@
 
 #include <algorithm>
 #include <mutex>
+#include <optional>
 #include <sstream>
 
-#include "BufferLayer.h"
-#include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
-#include "EffectLayer.h"
 #include "FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
 #include "LayerProtoHelper.h"
-#include "LayerRejecter.h"
-#include "MonitoredProducer.h"
 #include "MutexUtils.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
 
 #define DEBUG_RESIZE 0
+#define EARLY_RELEASE_ENABLED false
 
 namespace android {
 namespace {
 constexpr int kDumpTableRowLength = 159;
+
 const ui::Transform kIdentityTransform;
+
+bool assignTransform(ui::Transform* dst, ui::Transform& from) {
+    if (*dst == from) {
+        return false;
+    }
+    *dst = from;
+    return true;
+}
+
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+    const auto frameRateCompatibility = [frameRate] {
+        switch (frameRate.type) {
+            case Layer::FrameRateCompatibility::Default:
+                return FrameRateCompatibility::Default;
+            case Layer::FrameRateCompatibility::ExactOrMultiple:
+                return FrameRateCompatibility::ExactOrMultiple;
+            default:
+                return FrameRateCompatibility::Undefined;
+        }
+    }();
+
+    const auto seamlessness = [frameRate] {
+        switch (frameRate.seamlessness) {
+            case scheduler::Seamlessness::OnlySeamless:
+                return Seamlessness::ShouldBeSeamless;
+            case scheduler::Seamlessness::SeamedAndSeamless:
+                return Seamlessness::NotRequired;
+            default:
+                return Seamlessness::Undefined;
+        }
+    }();
+
+    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+                                       .frameRateCompatibility = frameRateCompatibility,
+                                       .seamlessness = seamlessness};
+}
+
 } // namespace
 
 using namespace ftl::flag_operators;
 
 using base::StringAppendF;
+using frontend::LayerSnapshot;
+using frontend::RoundedCornerState;
+using gui::GameMode;
+using gui::LayerMetadata;
 using gui::WindowInfo;
 
 using PresentState = frametimeline::SurfaceFrame::PresentState;
 
-std::atomic<int32_t> Layer::sSequence{1};
-
 Layer::Layer(const LayerCreationArgs& args)
-      : sequence(args.sequence.value_or(sSequence++)),
-        mFlinger(args.flinger),
+      : sequence(args.sequence),
+        mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
         mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
         mClientRef(args.client),
-        mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))),
-        mLayerCreationFlags(args.flags) {
+        mWindowType(static_cast<WindowInfo::Type>(
+                args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
+        mLayerCreationFlags(args.flags),
+        mBorderEnabled(false),
+        mTextureName(args.textureName),
+        mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
+    ALOGV("Creating Layer %s", getDebugName());
+
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
     if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
     if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
         layerFlags |= layer_state_t::eLayerSkipScreenshot;
-    if (args.sequence) {
-        sSequence = *args.sequence + 1;
-    }
     mDrawingState.flags = layerFlags;
-    mDrawingState.active_legacy.transform.set(0, 0);
     mDrawingState.crop.makeInvalid();
-    mDrawingState.requestedCrop = mDrawingState.crop;
     mDrawingState.z = 0;
     mDrawingState.color.a = 1.0f;
     mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
     mDrawingState.sequence = 0;
-    mDrawingState.requested_legacy = mDrawingState.active_legacy;
-    mDrawingState.width = UINT32_MAX;
-    mDrawingState.height = UINT32_MAX;
     mDrawingState.transform.set(0, 0);
     mDrawingState.frameNumber = 0;
+    mDrawingState.barrierFrameNumber = 0;
+    mDrawingState.producerId = 0;
+    mDrawingState.barrierProducerId = 0;
     mDrawingState.bufferTransform = 0;
     mDrawingState.transformToDisplayInverse = false;
     mDrawingState.crop.makeInvalid();
     mDrawingState.acquireFence = sp<Fence>::make(-1);
     mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
-    mDrawingState.dataspace = ui::Dataspace::UNKNOWN;
+    mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
     mDrawingState.hdrMetadata.validTypes = 0;
     mDrawingState.surfaceDamageRegion = Region::INVALID_REGION;
     mDrawingState.cornerRadius = 0.0f;
@@ -146,6 +193,7 @@
     mDrawingState.isTrustedOverlay = false;
     mDrawingState.dropInputMode = gui::DropInputMode::NONE;
     mDrawingState.dimmingEnabled = true;
+    mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
 
     if (args.flags & ISurfaceComposerClient::eNoColorFill) {
         // Set an invalid color so there is no color fill.
@@ -153,22 +201,22 @@
         mDrawingState.color.g = -1.0_hf;
         mDrawingState.color.b = -1.0_hf;
     }
-    CompositorTiming compositorTiming;
-    args.flinger->getCompositorTiming(&compositorTiming);
-    mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
 
-    mCallingPid = args.callingPid;
-    mCallingUid = args.callingUid;
+    mFrameTracker.setDisplayRefreshPeriod(
+            args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
 
-    if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
-        // If the system didn't send an ownerUid, use the callingUid for the ownerUid.
-        mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
-        mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid);
-    } else {
-        // A create layer request from a non system request cannot specify the owner uid
-        mOwnerUid = mCallingUid;
-        mOwnerPid = mCallingPid;
-    }
+    mOwnerUid = args.ownerUid;
+    mOwnerPid = args.ownerPid;
+
+    mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
+    mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
+    mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
+
+    mSnapshot->sequence = sequence;
+    mSnapshot->name = getDebugName();
+    mSnapshot->textureName = mTextureName;
+    mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
+    mSnapshot->parentTransform = {};
 }
 
 void Layer::onFirstRef() {
@@ -176,10 +224,28 @@
 }
 
 Layer::~Layer() {
-    sp<Client> c(mClientRef.promote());
-    if (c != 0) {
-        c->detachLayer(this);
+    LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId,
+                        "Layer destructor called off the main thread.");
+
+    // The original layer and the clone layer share the same texture and buffer. Therefore, only
+    // one of the layers, in this case the original layer, needs to handle the deletion. The
+    // original layer and the clone should be removed at the same time so there shouldn't be any
+    // issue with the clone layer trying to use the texture.
+    if (mBufferInfo.mBuffer != nullptr) {
+        callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                  mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
+                                  mBufferInfo.mFence);
     }
+    if (!isClone()) {
+        // The original layer and the clone layer share the same texture. Therefore, only one of
+        // the layers, in this case the original layer, needs to handle the deletion. The original
+        // layer and the clone should be removed at the same time so there shouldn't be any issue
+        // with the clone layer trying to use the deleted texture.
+        mFlinger->deleteTextureAsync(mTextureName);
+    }
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 
     mFrameTracker.logAndResetStats(mName);
     mFlinger->onLayerDestroyed(this);
@@ -188,33 +254,19 @@
         mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
     }
     if (mHadClonedChild) {
-        mFlinger->mNumClones--;
+        auto& roots = mFlinger->mLayerMirrorRoots;
+        roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end());
     }
-}
-
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
-                                     uint32_t flags, LayerMetadata metadata)
-      : flinger(flinger),
-        client(std::move(client)),
-        name(std::move(name)),
-        flags(flags),
-        metadata(std::move(metadata)) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    callingPid = ipc->getCallingPid();
-    callingUid = ipc->getCallingUid();
+    if (hasTrustedPresentationListener()) {
+        mFlinger->mNumTrustedPresentationListeners--;
+        updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/);
+    }
 }
 
 // ---------------------------------------------------------------------------
 // callbacks
 // ---------------------------------------------------------------------------
 
-/*
- * onLayerDisplayed is only meaningful for BufferLayer, but, is called through
- * Layer.  So, the implementation is done in BufferLayer.  When called on a
- * EffectLayer object, it's essentially a NOP.
- */
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult>) {}
-
 void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
     if (mDrawingState.zOrderRelativeOf == nullptr) {
         return;
@@ -227,7 +279,7 @@
     }
 
     if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) {
-        strongRelative->removeZOrderRelative(this);
+        strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
         mFlinger->setTransactionFlags(eTraversalNeeded);
         setZOrderRelativeOf(nullptr);
     }
@@ -238,14 +290,15 @@
         mRemovedFromDrawingState = true;
         mFlinger->mScheduler->deregisterLayer(this);
     }
+    updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/);
 
-    mFlinger->markLayerPendingRemovalLocked(this);
+    mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this));
 }
 
 sp<Layer> Layer::getRootLayer() {
     sp<Layer> parent = getParent();
     if (parent == nullptr) {
-        return this;
+        return sp<Layer>::fromExisting(this);
     }
     return parent->getRootLayer();
 }
@@ -290,7 +343,8 @@
         return nullptr;
     }
     mGetHandleCalled = true;
-    return new Handle(mFlinger, this);
+    mHandleAlive = true;
+    return sp<LayerHandle>::make(mFlinger, sp<Layer>::fromExisting(this));
 }
 
 // ---------------------------------------------------------------------------
@@ -337,6 +391,107 @@
     return reduce(mBounds, activeTransparentRegion);
 }
 
+// No early returns.
+void Layer::updateTrustedPresentationState(const DisplayDevice* display,
+                                           const frontend::LayerSnapshot* snapshot,
+                                           int64_t time_in_ms, bool leaveState) {
+    if (!hasTrustedPresentationListener()) {
+        return;
+    }
+    const bool lastState = mLastComputedTrustedPresentationState;
+    mLastComputedTrustedPresentationState = false;
+
+    if (!leaveState) {
+        const auto outputLayer = findOutputLayerForDisplay(display);
+        if (outputLayer != nullptr) {
+            if (outputLayer->getState().coveredRegionExcludingDisplayOverlays) {
+                Region coveredRegion =
+                        *outputLayer->getState().coveredRegionExcludingDisplayOverlays;
+                mLastComputedTrustedPresentationState =
+                        computeTrustedPresentationState(snapshot->geomLayerBounds,
+                                                        snapshot->sourceBounds(), coveredRegion,
+                                                        snapshot->transformedBounds,
+                                                        snapshot->alpha,
+                                                        snapshot->geomLayerTransform,
+                                                        mTrustedPresentationThresholds);
+            } else {
+                ALOGE("CoveredRegionExcludingDisplayOverlays was not set for %s. Don't compute "
+                      "TrustedPresentationState",
+                      getDebugName());
+            }
+        }
+    }
+    const bool newState = mLastComputedTrustedPresentationState;
+    if (lastState && !newState) {
+        // We were in the trusted presentation state, but now we left it,
+        // emit the callback if needed
+        if (mLastReportedTrustedPresentationState) {
+            mLastReportedTrustedPresentationState = false;
+            mTrustedPresentationListener.invoke(false);
+        }
+        // Reset the timer
+        mEnteredTrustedPresentationStateTime = -1;
+    } else if (!lastState && newState) {
+        // We were not in the trusted presentation state, but we entered it, begin the timer
+        // and make sure this gets called at least once more!
+        mEnteredTrustedPresentationStateTime = time_in_ms;
+        mFlinger->forceFutureUpdate(mTrustedPresentationThresholds.stabilityRequirementMs * 1.5);
+    }
+
+    // Has the timer elapsed, but we are still in the state? Emit a callback if needed
+    if (!mLastReportedTrustedPresentationState && newState &&
+        (time_in_ms - mEnteredTrustedPresentationStateTime >
+         mTrustedPresentationThresholds.stabilityRequirementMs)) {
+        mLastReportedTrustedPresentationState = true;
+        mTrustedPresentationListener.invoke(true);
+    }
+}
+
+/**
+ * See SurfaceComposerClient.h: setTrustedPresentationCallback for discussion
+ * of how the parameters and thresholds are interpreted. The general spirit is
+ * to produce an upper bound on the amount of the buffer which was presented.
+ */
+bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds,
+                                            const Region& coveredRegion,
+                                            const FloatRect& screenBounds, float alpha,
+                                            const ui::Transform& effectiveTransform,
+                                            const TrustedPresentationThresholds& thresholds) {
+    if (alpha < thresholds.minAlpha) {
+        return false;
+    }
+    if (sourceBounds.getWidth() == 0 || sourceBounds.getHeight() == 0) {
+        return false;
+    }
+    if (screenBounds.getWidth() == 0 || screenBounds.getHeight() == 0) {
+        return false;
+    }
+
+    const float sx = effectiveTransform.dsdx();
+    const float sy = effectiveTransform.dsdy();
+    float fractionRendered = std::min(sx * sy, 1.0f);
+
+    float boundsOverSourceW = bounds.getWidth() / (float)sourceBounds.getWidth();
+    float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight();
+    fractionRendered *= boundsOverSourceW * boundsOverSourceH;
+
+    Region tJunctionFreeRegion = Region::createTJunctionFreeRegion(coveredRegion);
+    // Compute the size of all the rects since they may be disconnected.
+    float coveredSize = 0;
+    for (auto rect = tJunctionFreeRegion.begin(); rect < tJunctionFreeRegion.end(); rect++) {
+        float size = rect->width() * rect->height();
+        coveredSize += size;
+    }
+
+    fractionRendered *= (1 - (coveredSize / (screenBounds.getWidth() * screenBounds.getHeight())));
+
+    if (fractionRendered < thresholds.minFractionRendered) {
+        return false;
+    }
+
+    return true;
+}
+
 void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
                           float parentShadowRadius) {
     const State& s(getDrawingState());
@@ -382,6 +537,10 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
     }
+
+    if (mPotentialCursor) {
+        prepareCursorCompositionState();
+    }
 }
 
 Rect Layer::getCroppedBufferSize(const State& s) const {
@@ -417,40 +576,43 @@
                                         : Hwc2::IComposerClient::BlendMode::COVERAGE;
     }
 
-    auto* compositionState = editCompositionState();
-    compositionState->outputFilter = getOutputFilter();
-    compositionState->isVisible = isVisible();
-    compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
-    compositionState->shadowRadius = mEffectiveShadowRadius;
+    // Please keep in sync with LayerSnapshotBuilder
+    auto* snapshot = editLayerSnapshot();
+    snapshot->outputFilter = getOutputFilter();
+    snapshot->isVisible = isVisible();
+    snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    snapshot->shadowRadius = mEffectiveShadowRadius;
 
-    compositionState->contentDirty = contentDirty;
+    snapshot->contentDirty = contentDirty;
     contentDirty = false;
 
-    compositionState->geomLayerBounds = mBounds;
-    compositionState->geomLayerTransform = getTransform();
-    compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
-    compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
-
-    compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState->alpha = alpha;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->stretchEffect = getStretchEffect();
+    snapshot->geomLayerBounds = mBounds;
+    snapshot->geomLayerTransform = getTransform();
+    snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
+    snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
+    snapshot->localTransform = getActiveTransform(drawingState);
+    snapshot->localTransformInverse = snapshot->localTransform.inverse();
+    snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    snapshot->alpha = alpha;
+    snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->stretchEffect = getStretchEffect();
 }
 
 void Layer::prepareGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->geomBufferSize = getBufferSize(drawingState);
-    compositionState->geomContentCrop = getBufferCrop();
-    compositionState->geomCrop = getCrop(drawingState);
-    compositionState->geomBufferTransform = getBufferTransform();
-    compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState->geomUsesSourceCrop = usesSourceCrop();
-    compositionState->isSecure = isSecure();
+    // Please keep in sync with LayerSnapshotBuilder
+    snapshot->geomBufferSize = getBufferSize(drawingState);
+    snapshot->geomContentCrop = getBufferCrop();
+    snapshot->geomCrop = getCrop(drawingState);
+    snapshot->geomBufferTransform = getBufferTransform();
+    snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+    snapshot->geomUsesSourceCrop = usesSourceCrop();
+    snapshot->isSecure = isSecure();
 
-    compositionState->metadata.clear();
+    snapshot->metadata.clear();
     const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
     for (const auto& [key, mandatory] : supportedMetadata) {
         const auto& genericLayerMetadataCompatibilityMap =
@@ -466,50 +628,97 @@
             continue;
         }
 
-        compositionState->metadata
-                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+        snapshot->metadata.emplace(key,
+                                   compositionengine::GenericLayerMetadataEntry{mandatory,
+                                                                                it->second});
     }
 }
 
 void Layer::preparePerFrameCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    // Please keep in sync with LayerSnapshotBuilder
+    auto* snapshot = editLayerSnapshot();
 
-    compositionState->forceClientComposition = false;
+    snapshot->forceClientComposition = false;
 
-    compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
-    compositionState->dataspace = getDataSpace();
-    compositionState->colorTransform = getColorTransform();
-    compositionState->colorTransformIsIdentity = !hasColorTransform();
-    compositionState->surfaceDamage = surfaceDamageRegion;
-    compositionState->hasProtectedContent = isProtected();
-    compositionState->dimmingEnabled = isDimmingEnabled();
+    snapshot->isColorspaceAgnostic = isColorSpaceAgnostic();
+    snapshot->dataspace = getDataSpace();
+    snapshot->colorTransform = getColorTransform();
+    snapshot->colorTransformIsIdentity = !hasColorTransform();
+    snapshot->surfaceDamage = surfaceDamageRegion;
+    snapshot->hasProtectedContent = isProtected();
+    snapshot->dimmingEnabled = isDimmingEnabled();
+    snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio();
+    snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio();
+    snapshot->cachingHint = getCachingHint();
 
     const bool usesRoundedCorners = hasRoundedCorners();
 
-    compositionState->isOpaque =
-            isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
+    snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
     // Rounded corners no longer force client composition, since we may use a
     // hole punch so that the layer will appear to have rounded corners.
     if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 ||
-        compositionState->stretchEffect.hasEffect()) {
-        compositionState->forceClientComposition = true;
+        snapshot->stretchEffect.hasEffect()) {
+        snapshot->forceClientComposition = true;
     }
     // If there are no visible region changes, we still need to update blur parameters.
-    compositionState->blurRegions = drawingState.blurRegions;
-    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    snapshot->blurRegions = drawingState.blurRegions;
+    snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
 
     // Layer framerate is used in caching decisions.
     // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
     // LayerFECompositionState where it would be visible to Flattener.
-    compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
+    snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
+
+    if (hasBufferOrSidebandStream()) {
+        preparePerFrameBufferCompositionState();
+    } else {
+        preparePerFrameEffectsCompositionState();
+    }
+}
+
+void Layer::preparePerFrameBufferCompositionState() {
+    // Please keep in sync with LayerSnapshotBuilder
+    auto* snapshot = editLayerSnapshot();
+    // Sideband layers
+    if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
+        return;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
+    } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
+        snapshot->compositionType =
+                aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
+    } else {
+        // Normal buffer layers
+        snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
+        snapshot->compositionType = mPotentialCursor
+                ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+                : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
+    }
+
+    snapshot->buffer = getBuffer();
+    snapshot->acquireFence = mBufferInfo.mFence;
+    snapshot->frameNumber = mBufferInfo.mFrameNumber;
+    snapshot->sidebandStreamHasFrame = false;
+}
+
+void Layer::preparePerFrameEffectsCompositionState() {
+    // Please keep in sync with LayerSnapshotBuilder
+    auto* snapshot = editLayerSnapshot();
+    snapshot->color = getColor();
+    snapshot->compositionType =
+            aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
 }
 
 void Layer::prepareCursorCompositionState() {
     const State& drawingState{getDrawingState()};
-    auto* compositionState = editCompositionState();
+    // Please keep in sync with LayerSnapshotBuilder
+    auto* snapshot = editLayerSnapshot();
 
     // Apply the layer's transform, followed by the display's global transform
     // Here we're guaranteed that the layer's transform preserves rects
@@ -518,52 +727,7 @@
     Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
     Rect frame(getTransform().transform(bounds));
 
-    compositionState->cursorFrame = frame;
-}
-
-sp<compositionengine::LayerFE> Layer::asLayerFE() const {
-    return const_cast<compositionengine::LayerFE*>(
-            static_cast<const compositionengine::LayerFE*>(this));
-}
-
-sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
-    return nullptr;
-}
-
-compositionengine::LayerFECompositionState* Layer::editCompositionState() {
-    return nullptr;
-}
-
-const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
-    return nullptr;
-}
-
-bool Layer::onPreComposition(nsecs_t) {
-    return false;
-}
-
-void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
-    using StateSubset = compositionengine::LayerFE::StateSubset;
-
-    switch (subset) {
-        case StateSubset::BasicGeometry:
-            prepareBasicGeometryCompositionState();
-            break;
-
-        case StateSubset::GeometryAndContent:
-            prepareBasicGeometryCompositionState();
-            prepareGeometryCompositionState();
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Content:
-            preparePerFrameCompositionState();
-            break;
-
-        case StateSubset::Cursor:
-            prepareCursorCompositionState();
-            break;
-    }
+    snapshot->cursorFrame = frame;
 }
 
 const char* Layer::getDebugName() const {
@@ -574,109 +738,6 @@
 // drawing...
 // ---------------------------------------------------------------------------
 
-std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    if (!getCompositionState()) {
-        return {};
-    }
-
-    FloatRect bounds = getBounds();
-    half alpha = getAlpha();
-
-    compositionengine::LayerFE::LayerSettings layerSettings;
-    layerSettings.geometry.boundaries = bounds;
-    layerSettings.geometry.positionTransform = getTransform().asMatrix4();
-
-    // skip drawing content if the targetSettings indicate the content will be occluded
-    const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
-    layerSettings.skipContentDraw = !drawContent;
-
-    if (hasColorTransform()) {
-        layerSettings.colorTransform = getColorTransform();
-    }
-
-    const auto roundedCornerState = getRoundedCornerState();
-    layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
-    layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
-
-    layerSettings.alpha = alpha;
-    layerSettings.sourceDataspace = getDataSpace();
-
-    // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
-    // We do this here instead of in buffer info so that dumpsys can still report layers that are
-    // using the 170M transfer.
-    if (mFlinger->mTreat170mAsSrgb &&
-        (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
-                HAL_DATASPACE_TRANSFER_SMPTE_170M) {
-        layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
-                (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
-                (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
-                HAL_DATASPACE_TRANSFER_SRGB);
-    }
-
-    layerSettings.whitePointNits = targetSettings.whitePointNits;
-    switch (targetSettings.blurSetting) {
-        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
-            break;
-        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
-            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
-            break;
-        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
-            layerSettings.blurRegions = getBlurRegions();
-            layerSettings.blurRegionTransform =
-                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
-            break;
-        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
-        default:
-            break;
-    }
-    layerSettings.stretchEffect = getStretchEffect();
-    // Record the name of the layer for debugging further down the stack.
-    layerSettings.name = getName();
-    return layerSettings;
-}
-
-void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
-                                          bool blackout) const {
-    layerSettings.source.buffer.buffer = nullptr;
-    layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
-    layerSettings.disableBlending = true;
-    layerSettings.bufferId = 0;
-    layerSettings.frameNumber = 0;
-
-    // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
-    layerSettings.alpha = blackout ? 1.0f : 0.0f;
-    layerSettings.name = getName();
-}
-
-// TODO(b/188891810): This method now only ever returns 0 or 1 layers so we should return
-// std::optional instead of a vector.  Additionally, we should consider removing
-// this method entirely in favor of calling prepareClientComposition directly.
-std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
-        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
-    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
-            prepareClientComposition(targetSettings);
-    // Nothing to render.
-    if (!layerSettings) {
-        return {};
-    }
-
-    // HWC requests to clear this layer.
-    if (targetSettings.clearContent) {
-        prepareClearClientComposition(*layerSettings, false /* blackout */);
-        return {*layerSettings};
-    }
-
-    // set the shadow for the layer if needed
-    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
-
-    return {*layerSettings};
-}
-
 aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
         const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display);
@@ -704,6 +765,40 @@
     return (p != nullptr) ? p->isSecure() : false;
 }
 
+void Layer::transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
+                                      std::vector<JankData>& jankData) {
+    if (mPendingJankClassifications.empty() ||
+        !mPendingJankClassifications.front()->getJankType()) {
+        return;
+    }
+
+    bool includeJankData = false;
+    for (const auto& handle : handles) {
+        for (const auto& cb : handle->callbackIds) {
+            if (cb.includeJankData) {
+                includeJankData = true;
+                break;
+            }
+        }
+
+        if (includeJankData) {
+            jankData.reserve(mPendingJankClassifications.size());
+            break;
+        }
+    }
+
+    while (!mPendingJankClassifications.empty() &&
+           mPendingJankClassifications.front()->getJankType()) {
+        if (includeJankData) {
+            std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame =
+                    mPendingJankClassifications.front();
+            jankData.emplace_back(
+                    JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
+        }
+        mPendingJankClassifications.pop_front();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // transaction
 // ----------------------------------------------------------------------------
@@ -724,7 +819,7 @@
 
     if (s.sequence != mLastCommittedTxSequence) {
         // invalidate and recompute the visible regions if needed
-         mLastCommittedTxSequence = s.sequence;
+        mLastCommittedTxSequence = s.sequence;
         flags |= eVisibleRegion;
         this->contentDirty = true;
 
@@ -732,6 +827,10 @@
         mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
     }
 
+    if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) {
+        mFlinger->mUpdateInputInfo = true;
+    }
+
     commitTransaction(mDrawingState);
 
     return flags;
@@ -744,7 +843,7 @@
         if (surfaceFrame->getPresentState() != PresentState::Presented) {
             // With applyPendingStates, we could end up having presented surfaceframes from previous
             // states
-            surfaceFrame->setPresentState(PresentState::Presented);
+            surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
             mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
         }
     }
@@ -761,16 +860,6 @@
     mTransactionFlags |= mask;
 }
 
-bool Layer::setPosition(float x, float y) {
-    if (mDrawingState.transform.tx() == x && mDrawingState.transform.ty() == y) return false;
-    mDrawingState.sequence++;
-    mDrawingState.transform.set(x, y);
-
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
 bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) {
     ssize_t idx = mCurrentChildren.indexOf(childLayer);
     if (idx < 0) {
@@ -810,7 +899,7 @@
     if (mDrawingState.zOrderRelativeOf != nullptr) {
         sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
         if (strongRelative != nullptr) {
-            strongRelative->removeZOrderRelative(this);
+            strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
         }
         setZOrderRelativeOf(nullptr);
     }
@@ -842,7 +931,7 @@
 }
 
 bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
-    sp<Layer> relative = fromHandle(relativeToHandle).promote();
+    sp<Layer> relative = LayerHandle::getLayer(relativeToHandle);
     if (relative == nullptr) {
         return false;
     }
@@ -868,10 +957,10 @@
 
     auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
     if (oldZOrderRelativeOf != nullptr) {
-        oldZOrderRelativeOf->removeZOrderRelative(this);
+        oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this));
     }
     setZOrderRelativeOf(relative);
-    relative->addZOrderRelative(this);
+    relative->addZOrderRelative(wp<Layer>::fromExisting(this));
 
     setTransactionFlags(eTransactionNeeded);
 
@@ -882,7 +971,7 @@
     if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false;
     mDrawingState.isTrustedOverlay = isTrustedOverlay;
     mDrawingState.modified = true;
-    mFlinger->mInputInfoChanged = true;
+    mFlinger->mUpdateInputInfo = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
@@ -895,20 +984,6 @@
     return (p != nullptr) && p->isTrustedOverlay();
 }
 
-bool Layer::setSize(uint32_t w, uint32_t h) {
-    if (mDrawingState.requested_legacy.w == w && mDrawingState.requested_legacy.h == h)
-        return false;
-    mDrawingState.requested_legacy.w = w;
-    mDrawingState.requested_legacy.h = h;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    // record the new size, from this point on, when the client request
-    // a buffer, it'll get the new size.
-    setDefaultBufferSize(mDrawingState.requested_legacy.w, mDrawingState.requested_legacy.h);
-    return true;
-}
-
 bool Layer::setAlpha(float alpha) {
     if (mDrawingState.color.a == alpha) return false;
     mDrawingState.sequence++;
@@ -980,20 +1055,10 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
-bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
-    ui::Transform t;
-    t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
-    mDrawingState.sequence++;
-    mDrawingState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-    mDrawingState.modified = true;
-
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
 
 bool Layer::setTransparentRegionHint(const Region& transparent) {
-    mDrawingState.requestedTransparentRegion_legacy = transparent;
+    mDrawingState.sequence++;
+    mDrawingState.transparentRegionHint = transparent;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
     return true;
@@ -1022,9 +1087,8 @@
 }
 
 bool Layer::setCrop(const Rect& crop) {
-    if (mDrawingState.requestedCrop == crop) return false;
+    if (mDrawingState.crop == crop) return false;
     mDrawingState.sequence++;
-    mDrawingState.requestedCrop = crop;
     mDrawingState.crop = crop;
 
     mDrawingState.modified = true;
@@ -1092,12 +1156,27 @@
     return Layer::PRIORITY_UNSET;
 }
 
+bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) {
+    if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
+    mDrawingState.defaultFrameRateCompatibility = compatibility;
+    mDrawingState.modified = true;
+    mFlinger->mScheduler->setDefaultFrameRateCompatibility(this);
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+scheduler::LayerInfo::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
+    return mDrawingState.defaultFrameRateCompatibility;
+}
+
 bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
     return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
 };
 
-ui::LayerStack Layer::getLayerStack() const {
-    if (const auto parent = mDrawingParent.promote()) {
+ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const {
+    bool useDrawing = state == LayerVector::StateSet::Drawing;
+    const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+    if (parent) {
         return parent->getLayerStack();
     }
     return getDrawingState().layerStack;
@@ -1156,6 +1235,28 @@
     return StretchEffect{};
 }
 
+bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) {
+    if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) {
+        return false;
+    }
+    mBorderEnabled = shouldEnable;
+    mBorderWidth = width;
+    mBorderColor = color;
+    return true;
+}
+
+bool Layer::isBorderEnabled() {
+    return mBorderEnabled;
+}
+
+float Layer::getBorderWidth() {
+    return mBorderWidth;
+}
+
+const half4& Layer::getBorderColor() {
+    return mBorderColor;
+}
+
 bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
     // The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
     const auto frameRate = [&] {
@@ -1167,7 +1268,7 @@
         return parentFrameRate;
     }();
 
-    *transactionNeeded |= setFrameRateForLayerTree(frameRate);
+    *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
 
     // The frame rate is propagated to the children
     bool childrenHaveFrameRate = false;
@@ -1181,12 +1282,12 @@
     if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
         childrenHaveFrameRate) {
         *transactionNeeded |=
-                setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+                setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
     }
 
     // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
     // the same reason we are allowing touch boost for those layers. See
-    // RefreshRateConfigs::getBestRefreshRate for more details.
+    // RefreshRateSelector::rankFrameRates for details.
     const auto layerVotedWithDefaultCompatibility =
             frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
     const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
@@ -1198,7 +1299,7 @@
 
 void Layer::updateTreeHasFrameRateVote() {
     const auto root = [&]() -> sp<Layer> {
-        sp<Layer> layer = this;
+        sp<Layer> layer = sp<Layer>::fromExisting(this);
         while (auto parent = layer->getParent()) {
             layer = parent;
         }
@@ -1294,7 +1395,7 @@
     surfaceFrame->setAcquireFenceTime(acquireFenceTime);
     surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
     mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
-    mLastLatchTime = currentLatchTime;
+    updateLastLatchTime(currentLatchTime);
 }
 
 std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
@@ -1329,12 +1430,11 @@
     if (fps) {
         surfaceFrame->setRenderRate(*fps);
     }
-    // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well.
     onSurfaceFrameCreated(surfaceFrame);
     return surfaceFrame;
 }
 
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
     if (mDrawingState.frameRateForLayerTree == frameRate) {
         return false;
     }
@@ -1347,9 +1447,21 @@
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-    mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
+    mFlinger->mScheduler
+            ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+                                 scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
+    return true;
+}
 
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+    if (mDrawingState.frameRateForLayerTree == frameRate) {
+        return false;
+    }
+
+    mDrawingState.frameRateForLayerTree = frameRate;
+    mFlinger->mScheduler
+            ->recordLayerHistory(sequence, layerProps, systemTime(),
+                                 scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
     return true;
 }
 
@@ -1391,12 +1503,16 @@
     return usage;
 }
 
+void Layer::skipReportingTransformHint() {
+    mSkipReportingTransformHint = true;
+}
+
 void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
     if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
         transformHint = ui::Transform::ROT_0;
     }
 
-    setTransformHint(transformHint);
+    setTransformHintLegacy(transformHint);
 }
 
 // ----------------------------------------------------------------------------
@@ -1404,16 +1520,15 @@
 // ----------------------------------------------------------------------------
 
 // TODO(marissaw): add new layer state info to layer debugging
-LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
+gui::LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
     using namespace std::string_literals;
 
-    LayerDebugInfo info;
+    gui::LayerDebugInfo info;
     const State& ds = getDrawingState();
     info.mName = getName();
     sp<Layer> parent = mDrawingParent.promote();
     info.mParentName = parent ? parent->getName() : "none"s;
     info.mType = getType();
-    info.mTransparentRegion = ds.activeTransparentRegion_legacy;
 
     info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
@@ -1421,8 +1536,6 @@
     info.mX = ds.transform.tx();
     info.mY = ds.transform.ty();
     info.mZ = ds.z;
-    info.mWidth = ds.width;
-    info.mHeight = ds.height;
     info.mCrop = ds.crop;
     info.mColor = ds.color;
     info.mFlags = ds.flags;
@@ -1535,9 +1648,10 @@
     mFrameTracker.getStats(outStats);
 }
 
-void Layer::dumpCallingUidPid(std::string& result) const {
-    StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
-                  getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
+void Layer::dumpOffscreenDebugInfo(std::string& result) const {
+    std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
+    StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(),
+                  mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : "");
 }
 
 void Layer::onDisconnect() {
@@ -1546,9 +1660,9 @@
     mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
-size_t Layer::getChildrenCount() const {
+size_t Layer::getDescendantCount() const {
     size_t count = 0;
-    for (const sp<Layer>& child : mCurrentChildren) {
+    for (const sp<Layer>& child : mDrawingChildren) {
         count += 1 + child->getChildrenCount();
     }
     return count;
@@ -1556,8 +1670,9 @@
 
 void Layer::setGameModeForTree(GameMode gameMode) {
     const auto& currentState = getDrawingState();
-    if (currentState.metadata.has(METADATA_GAME_MODE)) {
-        gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0));
+    if (currentState.metadata.has(gui::METADATA_GAME_MODE)) {
+        gameMode =
+                static_cast<GameMode>(currentState.metadata.getInt32(gui::METADATA_GAME_MODE, 0));
     }
     setGameMode(gameMode);
     for (const sp<Layer>& child : mCurrentChildren) {
@@ -1570,7 +1685,7 @@
     setTransactionFlags(eTransactionNeeded);
 
     mCurrentChildren.add(layer);
-    layer->setParent(this);
+    layer->setParent(sp<Layer>::fromExisting(this));
     layer->setGameModeForTree(mGameMode);
     updateTreeHasFrameRateVote();
 }
@@ -1602,7 +1717,7 @@
 bool Layer::reparent(const sp<IBinder>& newParentHandle) {
     sp<Layer> newParent;
     if (newParentHandle != nullptr) {
-        newParent = fromHandle(newParentHandle).promote();
+        newParent = LayerHandle::getLayer(newParentHandle);
         if (newParent == nullptr) {
             ALOGE("Unable to promote Layer handle");
             return false;
@@ -1615,11 +1730,11 @@
 
     sp<Layer> parent = getParent();
     if (parent != nullptr) {
-        parent->removeChild(this);
+        parent->removeChild(sp<Layer>::fromExisting(this));
     }
 
     if (newParentHandle != nullptr) {
-        newParent->addChild(this);
+        newParent->addChild(sp<Layer>::fromExisting(this));
         if (!newParent->isRemovedFromCurrentState()) {
             addToCurrentState();
         } else {
@@ -1794,6 +1909,12 @@
     }
 }
 
+void Layer::traverseChildren(const LayerVector::Visitor& visitor) {
+    for (const sp<Layer>& child : mDrawingChildren) {
+        visitor(child.get());
+    }
+}
+
 LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                              const std::vector<Layer*>& layersInTree) {
     LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
@@ -1901,8 +2022,11 @@
 }
 
 int32_t Layer::getBackgroundBlurRadius() const {
-    const auto& p = mDrawingParent.promote();
+    if (getDrawingState().backgroundBlurRadius == 0) {
+        return 0;
+    }
 
+    const auto& p = mDrawingParent.promote();
     half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
     return parentAlpha * getDrawingState().backgroundBlurRadius;
 }
@@ -1916,7 +2040,7 @@
     return regionsCopy;
 }
 
-Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+RoundedCornerState Layer::getRoundedCornerState() const {
     // Get parent settings
     RoundedCornerState parentSettings;
     const auto& parent = mDrawingParent.promote();
@@ -1957,39 +2081,6 @@
     return {};
 }
 
-void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
-                                           const Rect& layerStackRect) {
-    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
-
-    // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if
-    // transparent regions are present. This may not be necessary since shadows are only cast by
-    // SurfaceFlinger's EffectLayers, which do not typically use transparent regions.
-    state.boundaries = mBounds;
-
-    // Shift the spot light x-position to the middle of the display and then
-    // offset it by casting layer's screen pos.
-    state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left;
-    state.lightPos.y -= mScreenBounds.top;
-
-    state.length = mEffectiveShadowRadius;
-
-    if (state.length > 0.f) {
-        const float casterAlpha = caster.alpha;
-        const bool casterIsOpaque =
-                ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque);
-
-        // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
-        // Otherwise the generated shadow will only be shown around the casting layer.
-        state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
-        state.ambientColor *= casterAlpha;
-        state.spotColor *= casterAlpha;
-
-        if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) {
-            caster.shadow = state;
-        }
-    }
-}
-
 bool Layer::findInHierarchy(const sp<Layer>& l) {
     if (l == this) {
         return true;
@@ -2017,7 +2108,7 @@
                   zOrderRelativeOf->mName.c_str());
             ALOGE("Severing rel Z loop, potentially dangerous");
             mDrawingState.isRelativeOf = false;
-            zOrderRelativeOf->removeZOrderRelative(this);
+            zOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this));
         }
     }
 }
@@ -2025,9 +2116,10 @@
 
 void Layer::setInputInfo(const WindowInfo& info) {
     mDrawingState.inputInfo = info;
-    mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote());
+    mDrawingState.touchableRegionCrop =
+            LayerHandle::getLayer(info.touchableRegionCropHandle.promote());
     mDrawingState.modified = true;
-    mFlinger->mInputInfoChanged = true;
+    mFlinger->mUpdateInputInfo = true;
     setTransactionFlags(eTransactionNeeded);
 }
 
@@ -2037,15 +2129,9 @@
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
     if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
-        ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
-
-        // Only populate for the primary display.
-        if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
-            const auto compositionType = getCompositionType(*display);
-            layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
-            LayerProtoHelper::writeToProto(getVisibleRegion(display.get()),
-                                           [&]() { return layerProto->mutable_visible_region(); });
-        }
+        ui::LayerStack layerStack =
+                (mSnapshot) ? mSnapshot->outputFilter.layerStack : ui::INVALID_LAYER_STACK;
+        writeCompositionStateToProto(layerProto, layerStack);
     }
 
     for (const sp<Layer>& layer : mDrawingChildren) {
@@ -2055,6 +2141,19 @@
     return layerProto;
 }
 
+void Layer::writeCompositionStateToProto(LayerProto* layerProto, ui::LayerStack layerStack) {
+    ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
+    ftl::FakeGuard mainThreadGuard(kMainThreadContext);
+
+    // Only populate for the primary display.
+    if (const auto display = mFlinger->getDisplayFromLayerStack(layerStack)) {
+        const auto compositionType = getCompositionType(*display);
+        layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+        LayerProtoHelper::writeToProto(getVisibleRegion(display),
+                                       [&]() { return layerProto->mutable_visible_region(); });
+    }
+}
+
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo) {
     const ui::Transform transform = getTransform();
     auto buffer = getExternalTexture();
@@ -2069,8 +2168,6 @@
     layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
     layerInfo->set_queued_frames(getQueuedFrameCount());
     layerInfo->set_curr_frame(mCurrentFrameNumber);
-    layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
-
     layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
     layerInfo->set_corner_radius(
             (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
@@ -2119,7 +2216,7 @@
         }
     }
 
-    LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+    LayerProtoHelper::writeToProto(state.transparentRegionHint,
                                    [&]() { return layerInfo->mutable_transparent_region(); });
 
     layerInfo->set_layer_stack(getLayerStack().id);
@@ -2129,9 +2226,6 @@
         return layerInfo->mutable_requested_position();
     });
 
-    LayerProtoHelper::writeSizeToProto(state.width, state.height,
-                                       [&]() { return layerInfo->mutable_size(); });
-
     LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
 
     layerInfo->set_is_opaque(isOpaque(state));
@@ -2191,14 +2285,6 @@
     return mRemovedFromDrawingState;
 }
 
-ui::Transform Layer::getInputTransform() const {
-    return getTransform();
-}
-
-Rect Layer::getInputBounds() const {
-    return getCroppedBufferSize(getDrawingState());
-}
-
 // Applies the given transform to the region, while protecting against overflows caused by any
 // offsets. If applying the offset in the transform to any of the Rects in the region would result
 // in an overflow, they are not added to the output Region.
@@ -2231,62 +2317,21 @@
 }
 
 void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
-    Rect tmpBounds = getInputBounds();
-    if (!tmpBounds.isValid()) {
+    auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false);
+    if (!inputBoundsValid) {
         info.touchableRegion.clear();
-        // A layer could have invalid input bounds and still expect to receive touch input if it has
-        // replaceTouchableRegionWithCrop. For that case, the input transform needs to be calculated
-        // correctly to determine the coordinate space for input events. Use an empty rect so that
-        // the layer will receive input in its own layer space.
-        tmpBounds = Rect::EMPTY_RECT;
     }
 
-    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
-    // frame and transform used for the layer, which determines the bounds and the coordinate space
-    // within which the layer will receive input.
-    //
-    // The coordinate space within which each of the bounds are specified is explicitly documented
-    // in the variable name. For example "inputBoundsInLayer" is specified in layer space. A
-    // Transform converts one coordinate space to another, which is apparent in its naming. For
-    // example, "layerToDisplay" transforms layer space to display space.
-    //
-    // Coordinate space definitions:
-    //   - display: The display device's coordinate space. Correlates to pixels on the display.
-    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
-    //   - layer: The coordinate space of this layer.
-    //   - input: The coordinate space in which this layer will receive input events. This could be
-    //            different than layer space if a surfaceInset is used, which changes the origin
-    //            of the input space.
-    const FloatRect inputBoundsInLayer = tmpBounds.toFloatRect();
-
-    // Clamp surface inset to the input bounds.
-    const auto surfaceInset = static_cast<float>(info.surfaceInset);
-    const float xSurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getWidth() / 2.f));
-    const float ySurfaceInset =
-            std::max(0.f, std::min(surfaceInset, inputBoundsInLayer.getHeight() / 2.f));
-
-    // Apply the insets to the input bounds.
-    const FloatRect insetBoundsInLayer(inputBoundsInLayer.left + xSurfaceInset,
-                                       inputBoundsInLayer.top + ySurfaceInset,
-                                       inputBoundsInLayer.right - xSurfaceInset,
-                                       inputBoundsInLayer.bottom - ySurfaceInset);
-
-    // Crop the input bounds to ensure it is within the parent's bounds.
-    const FloatRect croppedInsetBoundsInLayer = mBounds.intersect(insetBoundsInLayer);
-
-    const ui::Transform layerToScreen = getInputTransform();
-    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
-
-    const Rect roundedFrameInDisplay{layerToDisplay.transform(croppedInsetBoundsInLayer)};
+    const Rect roundedFrameInDisplay = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
     info.frameLeft = roundedFrameInDisplay.left;
     info.frameTop = roundedFrameInDisplay.top;
     info.frameRight = roundedFrameInDisplay.right;
     info.frameBottom = roundedFrameInDisplay.bottom;
 
     ui::Transform inputToLayer;
-    inputToLayer.set(insetBoundsInLayer.left, insetBoundsInLayer.top);
-    const ui::Transform inputToDisplay = layerToDisplay * inputToLayer;
+    inputToLayer.set(inputBounds.left, inputBounds.top);
+    const ui::Transform layerToScreen = getInputTransform();
+    const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
 
     // InputDispatcher expects a display-to-input transform.
     info.transform = inputToDisplay.inverse();
@@ -2297,7 +2342,7 @@
 }
 
 void Layer::fillTouchOcclusionMode(WindowInfo& info) {
-    sp<Layer> p = this;
+    sp<Layer> p = sp<Layer>::fromExisting(this);
     while (p != nullptr && !p->hasInputInfo()) {
         p = p->mDrawingParent.promote();
     }
@@ -2410,13 +2455,23 @@
         info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
     }
 
-    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
-        const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds);
-        info.touchableRegion = Region(displayTransform.transform(bounds));
+        Rect inputBoundsInDisplaySpace;
+        if (!cropLayer) {
+            FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        } else {
+            FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
+            inputBoundsInDisplaySpace =
+                    cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        }
+        info.touchableRegion = Region(inputBoundsInDisplaySpace);
     } else if (cropLayer != nullptr) {
-        info.touchableRegion = info.touchableRegion.intersect(
-                displayTransform.transform(Rect{cropLayer->mScreenBounds}));
+        FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
+        Rect inputBoundsInDisplaySpace =
+                cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
+        info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace);
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2428,7 +2483,7 @@
     // 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.
     if (isClone()) {
-        info.isClone = true;
+        info.inputConfig |= WindowInfo::InputConfig::CLONE;
         if (const sp<Layer> clonedRoot = getClonedRoot()) {
             const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds});
             info.touchableRegion = info.touchableRegion.intersect(rect);
@@ -2438,9 +2493,30 @@
     return info;
 }
 
+Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds,
+                                         const ui::Transform& screenToDisplay) {
+    // InputDispatcher works in the display device's coordinate space. Here, we calculate the
+    // frame and transform used for the layer, which determines the bounds and the coordinate space
+    // within which the layer will receive input.
+
+    // Coordinate space definitions:
+    //   - display: The display device's coordinate space. Correlates to pixels on the display.
+    //   - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
+    //   - layer: The coordinate space of this layer.
+    //   - input: The coordinate space in which this layer will receive input events. This could be
+    //            different than layer space if a surfaceInset is used, which changes the origin
+    //            of the input space.
+
+    // Crop the input bounds to ensure it is within the parent's bounds.
+    const FloatRect croppedInputBounds = mBounds.intersect(inputBounds);
+    const ui::Transform layerToScreen = getInputTransform();
+    const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
+    return Rect{layerToDisplay.transform(croppedInputBounds)};
+}
+
 sp<Layer> Layer::getClonedRoot() {
     if (mClonedChild != nullptr) {
-        return this;
+        return sp<Layer>::fromExisting(this);
     }
     if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
         return nullptr;
@@ -2453,14 +2529,23 @@
             mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
 }
 
-bool Layer::canReceiveInput() const {
-    return !isHiddenByPolicy();
-}
-
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
         const DisplayDevice* display) const {
     if (!display) return nullptr;
-    return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
+    if (!mFlinger->mLayerLifecycleManagerEnabled) {
+        return display->getCompositionDisplay()->getOutputLayerForLayer(
+                getCompositionEngineLayerFE());
+    }
+    sp<LayerFE> layerFE;
+    frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)};
+    for (auto& [p, layer] : mLayerFEs) {
+        if (p == path) {
+            layerFE = layer;
+        }
+    }
+
+    if (!layerFE) return nullptr;
+    return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE);
 }
 
 Region Layer::getVisibleRegion(const DisplayDevice* display) const {
@@ -2468,12 +2553,49 @@
     return outputLayer ? outputLayer->getState().visibleRegion : Region();
 }
 
-void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
+    mSnapshot->path.id = clonedFrom->getSequence();
+    mSnapshot->path.mirrorRootId = mirrorRootId;
+
     cloneDrawingState(clonedFrom.get());
     mClonedFrom = clonedFrom;
+    mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
+    mPotentialCursor = clonedFrom->mPotentialCursor;
+    mProtectedByApp = clonedFrom->mProtectedByApp;
+    updateCloneBufferInfo();
 }
 
-void Layer::updateMirrorInfo() {
+void Layer::updateCloneBufferInfo() {
+    if (!isClone() || !isClonedFromAlive()) {
+        return;
+    }
+
+    sp<Layer> clonedFrom = getClonedFrom();
+    mBufferInfo = clonedFrom->mBufferInfo;
+    mSidebandStream = clonedFrom->mSidebandStream;
+    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+    // After buffer info is updated, the drawingState from the real layer needs to be copied into
+    // the cloned. This is because some properties of drawingState can change when latchBuffer is
+    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+    // the cloned drawingState again.
+    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+    WindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+    cloneDrawingState(clonedFrom.get());
+
+    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+    mDrawingState.inputInfo = tmpInputInfo;
+}
+
+bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) {
     if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
         // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
         // it means that there is a clone, but the layer it was cloned from has been destroyed. In
@@ -2481,7 +2603,7 @@
         // destroyed. The root, this layer, will still be around since the client can continue
         // to hold a reference, but no cloned layers will be displayed.
         mClonedChild = nullptr;
-        return;
+        return true;
     }
 
     std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
@@ -2494,8 +2616,15 @@
     }
 
     mClonedChild->updateClonedDrawingState(clonedLayersMap);
-    mClonedChild->updateClonedChildren(this, clonedLayersMap);
+    mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap);
     mClonedChild->updateClonedRelatives(clonedLayersMap);
+
+    for (Layer* root : cloneRootsPendingUpdates) {
+        if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) {
+            return false;
+        }
+    }
+    return true;
 }
 
 void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
@@ -2505,7 +2634,7 @@
     if (isClonedFromAlive()) {
         sp<Layer> clonedFrom = getClonedFrom();
         cloneDrawingState(clonedFrom.get());
-        clonedLayersMap.emplace(clonedFrom, this);
+        clonedLayersMap.emplace(clonedFrom, sp<Layer>::fromExisting(this));
     }
 
     // The clone layer may have children in drawingState since they may have been created and
@@ -2535,7 +2664,7 @@
         }
         sp<Layer> clonedChild = clonedLayersMap[child];
         if (clonedChild == nullptr) {
-            clonedChild = child->createClone();
+            clonedChild = child->createClone(mirrorRoot->getSequence());
             clonedLayersMap[child] = clonedChild;
         }
         addChildToDrawing(clonedChild);
@@ -2549,7 +2678,7 @@
         if (clonedLayersMap.count(cropLayer) == 0) {
             // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
             // self as crop layer to avoid going outside bounds.
-            mDrawingState.touchableRegionCrop = this;
+            mDrawingState.touchableRegionCrop = wp<Layer>::fromExisting(this);
         } else {
             const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
             mDrawingState.touchableRegionCrop = clonedCropLayer;
@@ -2561,7 +2690,7 @@
 }
 
 void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
-    mDrawingState.zOrderRelativeOf = nullptr;
+    mDrawingState.zOrderRelativeOf = wp<Layer>();
     mDrawingState.zOrderRelatives.clear();
 
     if (!isClonedFromAlive()) {
@@ -2597,7 +2726,7 @@
 
 void Layer::addChildToDrawing(const sp<Layer>& layer) {
     mDrawingChildren.add(layer);
-    layer->mDrawingParent = this;
+    layer->mDrawingParent = sp<Layer>::fromExisting(this);
 }
 
 Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) {
@@ -2608,6 +2737,8 @@
             return FrameRateCompatibility::ExactOrMultiple;
         case ANATIVEWINDOW_FRAME_RATE_EXACT:
             return FrameRateCompatibility::Exact;
+        case ANATIVEWINDOW_FRAME_RATE_MIN:
+            return FrameRateCompatibility::Min;
         case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
             return FrameRateCompatibility::NoVote;
         default:
@@ -2641,24 +2772,7 @@
 void Layer::setClonedChild(const sp<Layer>& clonedChild) {
     mClonedChild = clonedChild;
     mHadClonedChild = true;
-    mFlinger->mNumClones++;
-}
-
-const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle");
-
-wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) {
-    if (handleBinder == nullptr) {
-        return nullptr;
-    }
-
-    BBinder* b = handleBinder->localBinder();
-    if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) {
-        return nullptr;
-    }
-
-    // We can safely cast this binder since its local and we verified its interface descriptor.
-    sp<Handle> handle = static_cast<Handle*>(handleBinder.get());
-    return handle->owner;
+    mFlinger->mLayerMirrorRoots.push_back(this);
 }
 
 bool Layer::setDropInputMode(gui::DropInputMode mode) {
@@ -2673,21 +2787,1499 @@
     mDrawingState = from->mDrawingState;
     // Skip callback info since they are not applicable for cloned layers.
     mDrawingState.releaseBufferListener = nullptr;
+    // TODO (b/238781169) currently broken for mirror layers because we do not
+    // track release fences for mirror layers composed on other displays
     mDrawingState.callbackHandles = {};
 }
 
-bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) {
-    if (handles.empty()) {
+void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                                      const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                                      const sp<Fence>& releaseFence) {
+    if (!listener) {
+        return;
+    }
+    ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
+    uint32_t currentMaxAcquiredBufferCount =
+            mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+    listener->onReleaseBuffer({buffer->getId(), framenumber},
+                              releaseFence ? releaseFence : Fence::NO_FENCE,
+                              currentMaxAcquiredBufferCount);
+}
+
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+                             ui::LayerStack layerStack) {
+    // If we are displayed on multiple displays in a single composition cycle then we would
+    // need to do careful tracking to enable the use of the mLastClientCompositionFence.
+    //  For example we can only use it if all the displays are client comp, and we need
+    //  to merge all the client comp fences. We could do this, but for now we just
+    // disable the optimization when a layer is composed on multiple displays.
+    if (mClearClientCompositionFenceOnLayerDisplayed) {
+        mLastClientCompositionFence = nullptr;
+    } else {
+        mClearClientCompositionFenceOnLayerDisplayed = true;
+    }
+
+    // The previous release fence notifies the client that SurfaceFlinger is done with the previous
+    // buffer that was presented on this layer. The first transaction that came in this frame that
+    // replaced the previous buffer on this layer needs this release fence, because the fence will
+    // let the client know when that previous buffer is removed from the screen.
+    //
+    // Every other transaction on this layer does not need a release fence because no other
+    // Transactions that were set on this layer this frame are going to have their preceding buffer
+    // removed from the display this frame.
+    //
+    // For example, if we have 3 transactions this frame. The first transaction doesn't contain a
+    // buffer so it doesn't need a previous release fence because the layer still needs the previous
+    // buffer. The second transaction contains a buffer so it needs a previous release fence because
+    // the previous buffer will be released this frame. The third transaction also contains a
+    // buffer. It replaces the buffer in the second transaction. The buffer in the second
+    // transaction will now no longer be presented so it is released immediately and the third
+    // transaction doesn't need a previous release fence.
+    sp<CallbackHandle> ch;
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
+            ch = handle;
+            break;
+        }
+    }
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
+
+    if (ch != nullptr) {
+        ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+        ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
+        ch->name = mName;
+    }
+    mPreviouslyPresentedLayerStacks.push_back(layerStack);
+}
+
+void Layer::onSurfaceFrameCreated(
+        const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
+    while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
+        // Too many SurfaceFrames pending classification. The front of the deque is probably not
+        // tracked by FrameTimeline and will never be presented. This will only result in a memory
+        // leak.
+        if (hasBufferOrSidebandStreamInDrawing()) {
+            // Only log for layers with a buffer, since we expect the jank data to be drained for
+            // these, while there may be no jank listeners for bufferless layers.
+            ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+                  mName.c_str());
+            std::string miniDump = mPendingJankClassifications.front()->miniDump();
+            ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+        }
+        mPendingJankClassifications.pop_front();
+    }
+    mPendingJankClassifications.emplace_back(surfaceFrame);
+}
+
+void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        if (mFlinger->mLayerLifecycleManagerEnabled) {
+            handle->transformHint = mTransformHint;
+        } else {
+            handle->transformHint = mSkipReportingTransformHint
+                    ? std::nullopt
+                    : std::make_optional<uint32_t>(mTransformHintLegacy);
+        }
+        handle->dequeueReadyTime = dequeueReadyTime;
+        handle->currentMaxAcquiredBufferCount =
+                mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
+        ATRACE_FORMAT_INSTANT("releasePendingBuffer %s - %" PRIu64, getDebugName(),
+                              handle->previousReleaseCallbackId.framenumber);
+    }
+
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->releasePreviousBuffer && mPreviousReleaseBufferEndpoint == handle->listener) {
+            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+            break;
+        }
+    }
+
+    std::vector<JankData> jankData;
+    transferAvailableJankData(mDrawingState.callbackHandles, jankData);
+    mFlinger->getTransactionCallbackInvoker().addCallbackHandles(mDrawingState.callbackHandles,
+                                                                 jankData);
+    mDrawingState.callbackHandles = {};
+}
+
+bool Layer::willPresentCurrentTransaction() const {
+    // Returns true if the most recent Transaction applied to CurrentState will be presented.
+    return (getSidebandStreamChanged() || getAutoRefresh() ||
+            (mDrawingState.modified &&
+             (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
+}
+
+bool Layer::setTransform(uint32_t transform) {
+    if (mDrawingState.bufferTransform == transform) return false;
+    mDrawingState.bufferTransform = transform;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) {
+    if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
+    mDrawingState.sequence++;
+    mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setBufferCrop(const Rect& bufferCrop) {
+    if (mDrawingState.bufferCrop == bufferCrop) return false;
+
+    mDrawingState.sequence++;
+    mDrawingState.bufferCrop = bufferCrop;
+
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setDestinationFrame(const Rect& destinationFrame) {
+    if (mDrawingState.destinationFrame == destinationFrame) return false;
+
+    mDrawingState.sequence++;
+    mDrawingState.destinationFrame = destinationFrame;
+
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+// Translate destination frame into scale and position. If a destination frame is not set, use the
+// provided scale and position
+bool Layer::updateGeometry() {
+    if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
+        mDrawingState.destinationFrame.isEmpty()) {
+        // If destination frame is not set, use the requested transform set via
+        // Layer::setPosition and Layer::setMatrix.
+        return assignTransform(&mDrawingState.transform, mRequestedTransform);
+    }
+
+    Rect destRect = mDrawingState.destinationFrame;
+    int32_t destW = destRect.width();
+    int32_t destH = destRect.height();
+    if (destRect.left < 0) {
+        destRect.left = 0;
+        destRect.right = destW;
+    }
+    if (destRect.top < 0) {
+        destRect.top = 0;
+        destRect.bottom = destH;
+    }
+
+    if (!mDrawingState.buffer) {
+        ui::Transform t;
+        t.set(destRect.left, destRect.top);
+        return assignTransform(&mDrawingState.transform, t);
+    }
+
+    uint32_t bufferWidth = mDrawingState.buffer->getWidth();
+    uint32_t bufferHeight = mDrawingState.buffer->getHeight();
+    // Undo any transformations on the buffer.
+    if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+    uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
+    if (mDrawingState.transformToDisplayInverse) {
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    float sx = destW / static_cast<float>(bufferWidth);
+    float sy = destH / static_cast<float>(bufferHeight);
+    ui::Transform t;
+    t.set(sx, 0, 0, sy);
+    t.set(destRect.left, destRect.top);
+    return assignTransform(&mDrawingState.transform, t);
+}
+
+bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
+    if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
+        mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
         return false;
     }
 
+    mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
+
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
+bool Layer::setPosition(float x, float y) {
+    if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
+        return false;
+    }
+
+    mRequestedTransform.set(x, y);
+
+    mDrawingState.sequence++;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+
+    return true;
+}
+
+void Layer::resetDrawingStateBufferInfo() {
+    mDrawingState.producerId = 0;
+    mDrawingState.frameNumber = 0;
+    mDrawingState.releaseBufferListener = nullptr;
+    mDrawingState.buffer = nullptr;
+    mDrawingState.acquireFence = sp<Fence>::make(-1);
+    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+    mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+    mDrawingState.releaseBufferEndpoint = nullptr;
+}
+
+bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                      const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+                      bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
+                      const FrameTimelineInfo& info) {
+    ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
+
+    const bool frameNumberChanged =
+            bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
+    const uint64_t frameNumber =
+            frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+    ATRACE_FORMAT_INSTANT("setBuffer %s - %" PRIu64, getDebugName(), frameNumber);
+
+    if (mDrawingState.buffer) {
+        mReleasePreviousBuffer = true;
+        if (!mBufferInfo.mBuffer ||
+            (!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
+             mDrawingState.frameNumber != mBufferInfo.mFrameNumber)) {
+            // If mDrawingState has a buffer, and we are about to update again
+            // before swapping to drawing state, then the first buffer will be
+            // dropped and we should decrement the pending buffer count and
+            // call any release buffer callbacks if set.
+            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mDrawingState.acquireFence);
+            decrementPendingBufferCount();
+            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
+                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
+                addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
+                mDrawingState.bufferSurfaceFrameTX.reset();
+            }
+        } else if (EARLY_RELEASE_ENABLED && mLastClientCompositionFence != nullptr) {
+            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+                                      mLastClientCompositionFence);
+            mLastClientCompositionFence = nullptr;
+        }
+    } else if (buffer) {
+        // if we are latching a buffer for the first time then clear the mLastLatchTime since
+        // we don't want to incorrectly classify a frame if we miss the desired present time.
+        updateLastLatchTime(0);
+    }
+
+    mDrawingState.desiredPresentTime = desiredPresentTime;
+    mDrawingState.isAutoTimestamp = isAutoTimestamp;
+    mDrawingState.latchedVsyncId = info.vsyncId;
+    mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection;
+    mDrawingState.modified = true;
+    if (!buffer) {
+        resetDrawingStateBufferInfo();
+        setTransactionFlags(eTransactionNeeded);
+        mDrawingState.bufferSurfaceFrameTX = nullptr;
+        setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+        return true;
+    }
+
+    mDrawingState.producerId = bufferData.producerId;
+    mDrawingState.barrierProducerId =
+            std::max(mDrawingState.producerId, mDrawingState.barrierProducerId);
+    mDrawingState.frameNumber = frameNumber;
+    mDrawingState.barrierFrameNumber =
+            std::max(mDrawingState.frameNumber, mDrawingState.barrierFrameNumber);
+
+    // TODO(b/277265947) log and flush transaction trace when we detect out of order updates
+    mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
+    mDrawingState.buffer = std::move(buffer);
+    mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
+            ? bufferData.acquireFence
+            : Fence::NO_FENCE;
+    mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+    if (mDrawingState.acquireFenceTime->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
+        // We latched this buffer unsiganled, so we need to pass the acquire fence
+        // on the callback instead of just the acquire time, since it's unknown at
+        // this point.
+        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFence;
+    } else {
+        mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime();
+    }
+    setTransactionFlags(eTransactionNeeded);
+
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
+                                      mOwnerUid, postTime, getGameMode());
+
+    if (mFlinger->mLegacyFrontEndEnabled) {
+        recordLayerHistoryBufferUpdate(getLayerProps());
+    }
+
+    setFrameTimelineVsyncForBufferTransaction(info, postTime);
+
+    if (dequeueTime && *dequeueTime != 0) {
+        const uint64_t bufferId = mDrawingState.buffer->getId();
+        mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+                                               FrameTracer::FrameEvent::DEQUEUE);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
+                                               FrameTracer::FrameEvent::QUEUE);
+    }
+
+    mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
+    return true;
+}
+
+void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) {
+    mDrawingState.desiredPresentTime = desiredPresentTime;
+    mDrawingState.isAutoTimestamp = isAutoTimestamp;
+}
+
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+    ATRACE_CALL();
+    const nsecs_t presentTime = [&] {
+        if (!mDrawingState.isAutoTimestamp) {
+            ATRACE_FORMAT_INSTANT("desiredPresentTime");
+            return mDrawingState.desiredPresentTime;
+        }
+
+        if (mDrawingState.useVsyncIdForRefreshRateSelection) {
+            const auto prediction =
+                    mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                            mDrawingState.latchedVsyncId);
+            if (prediction.has_value()) {
+                ATRACE_FORMAT_INSTANT("predictedPresentTime");
+                return prediction->presentTime;
+            }
+        }
+
+        return static_cast<nsecs_t>(0);
+    }();
+
+    if (ATRACE_ENABLED() && presentTime > 0) {
+        const auto presentIn = TimePoint::fromNs(presentTime) - TimePoint::now();
+        ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
+    }
+
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+                                             scheduler::LayerHistory::LayerUpdateType::Buffer);
+}
+
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+    const nsecs_t presentTime =
+            mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
+    mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+                                             scheduler::LayerHistory::LayerUpdateType::AnimationTX);
+}
+
+bool Layer::setDataspace(ui::Dataspace dataspace) {
+    if (mDrawingState.dataspace == dataspace) return false;
+    mDrawingState.dataspace = dataspace;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio) {
+    if (mDrawingState.currentHdrSdrRatio == currentBufferRatio &&
+        mDrawingState.desiredHdrSdrRatio == desiredRatio)
+        return false;
+    mDrawingState.currentHdrSdrRatio = currentBufferRatio;
+    mDrawingState.desiredHdrSdrRatio = desiredRatio;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setCachingHint(gui::CachingHint cachingHint) {
+    if (mDrawingState.cachingHint == cachingHint) return false;
+    mDrawingState.cachingHint = cachingHint;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
+    if (mDrawingState.hdrMetadata == hdrMetadata) return false;
+    mDrawingState.hdrMetadata = hdrMetadata;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
+    if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
+    mDrawingState.surfaceDamageRegion = surfaceDamage;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setApi(int32_t api) {
+    if (mDrawingState.api == api) return false;
+    mDrawingState.api = api;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
+    if (mDrawingState.sidebandStream == sidebandStream) return false;
+
+    if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+    } else if (sidebandStream != nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
+    }
+
+    mDrawingState.sidebandStream = sidebandStream;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    if (!mSidebandStreamChanged.exchange(true)) {
+        // mSidebandStreamChanged was false
+        mFlinger->onLayerUpdate();
+    }
+    return true;
+}
+
+bool Layer::setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles,
+                                             bool willPresent) {
+    // If there is no handle, we will not send a callback so reset mReleasePreviousBuffer and return
+    if (handles.empty()) {
+        mReleasePreviousBuffer = false;
+        return false;
+    }
+
+    std::deque<sp<CallbackHandle>> remainingHandles;
     for (const auto& handle : handles) {
-        mFlinger->getTransactionCallbackInvoker().registerUnpresentedCallbackHandle(handle);
+        // If this transaction set a buffer on this layer, release its previous buffer
+        handle->releasePreviousBuffer = mReleasePreviousBuffer;
+
+        // If this layer will be presented in this frame
+        if (willPresent) {
+            // If this transaction set an acquire fence on this layer, set its acquire time
+            handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
+            handle->frameNumber = mDrawingState.frameNumber;
+
+            // Store so latched time and release fence can be set
+            mDrawingState.callbackHandles.push_back(handle);
+
+        } else { // If this layer will NOT need to be relatched and presented this frame
+            // Queue this handle to be notified below.
+            remainingHandles.push_back(handle);
+        }
+    }
+
+    if (!remainingHandles.empty()) {
+        // Notify the transaction completed threads these handles are done. These are only the
+        // handles that were not added to the mDrawingState, which will be notified later.
+        std::vector<JankData> jankData;
+        transferAvailableJankData(remainingHandles, jankData);
+        mFlinger->getTransactionCallbackInvoker().addCallbackHandles(remainingHandles, jankData);
+    }
+
+    mReleasePreviousBuffer = false;
+    mCallbackHandleAcquireTimeOrFence = -1;
+
+    return willPresent;
+}
+
+Rect Layer::getBufferSize(const State& /*s*/) const {
+    // for buffer state layers we use the display frame size as the buffer size.
+
+    if (mBufferInfo.mBuffer == nullptr) {
+        return Rect::INVALID_RECT;
+    }
+
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+
+    // Undo any transformations on the buffer and return the result.
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
+        std::swap(bufWidth, bufHeight);
+    }
+
+    if (getTransformToDisplayInverse()) {
+        uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufWidth, bufHeight);
+        }
+    }
+
+    return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
+}
+
+FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const {
+    if (mBufferInfo.mBuffer == nullptr) {
+        return parentBounds;
+    }
+
+    return getBufferSize(getDrawingState()).toFloatRect();
+}
+
+bool Layer::fenceHasSignaled() const {
+    if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
+        return true;
+    }
+
+    const bool fenceSignaled =
+            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
+}
+
+void Layer::onPreComposition(nsecs_t refreshStartTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->refreshStartTime = refreshStartTime;
+    }
+}
+
+void Layer::setAutoRefresh(bool autoRefresh) {
+    mDrawingState.autoRefresh = autoRefresh;
+}
+
+bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
+    // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
+    auto* snapshot = editLayerSnapshot();
+    snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
+
+    if (mSidebandStreamChanged.exchange(false)) {
+        const State& s(getDrawingState());
+        // mSidebandStreamChanged was true
+        mSidebandStream = s.sidebandStream;
+        snapshot->sidebandStream = mSidebandStream;
+        if (mSidebandStream != nullptr) {
+            setTransactionFlags(eTransactionNeeded);
+            mFlinger->setTransactionFlags(eTraversalNeeded);
+        }
+        recomputeVisibleRegions = true;
+
+        return true;
+    }
+    return false;
+}
+
+bool Layer::hasFrameUpdate() const {
+    const State& c(getDrawingState());
+    return (mDrawingStateModified || mDrawingState.modified) &&
+            (c.buffer != nullptr || c.bgColorLayer != nullptr);
+}
+
+void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
+    const State& s(getDrawingState());
+
+    if (!s.buffer) {
+        if (bgColorOnly || mBufferInfo.mBuffer) {
+            for (auto& handle : mDrawingState.callbackHandles) {
+                handle->latchTime = latchTime;
+            }
+        }
+        return;
+    }
+
+    for (auto& handle : mDrawingState.callbackHandles) {
+        if (handle->frameNumber == mDrawingState.frameNumber) {
+            handle->latchTime = latchTime;
+        }
+    }
+
+    const int32_t layerId = getSequence();
+    const uint64_t bufferId = mDrawingState.buffer->getId();
+    const uint64_t frameNumber = mDrawingState.frameNumber;
+    const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+    mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence);
+    mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime);
+
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence,
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime,
+                                           FrameTracer::FrameEvent::LATCH);
+
+    auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+    if (bufferSurfaceFrame != nullptr &&
+        bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+        // Update only if the bufferSurfaceFrame wasn't already presented. A Presented
+        // bufferSurfaceFrame could be seen here if a pending state was applied successfully and we
+        // are processing the next state.
+        addSurfaceFramePresentedForBuffer(bufferSurfaceFrame,
+                                          mDrawingState.acquireFenceTime->getSignalTime(),
+                                          latchTime);
+        mDrawingState.bufferSurfaceFrameTX.reset();
+    }
+
+    std::deque<sp<CallbackHandle>> remainingHandles;
+    mFlinger->getTransactionCallbackInvoker()
+            .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+    mDrawingState.callbackHandles = remainingHandles;
+
+    mDrawingStateModified = false;
+}
+
+void Layer::gatherBufferInfo() {
+    mPreviousReleaseCallbackId = {getCurrentBufferId(), mBufferInfo.mFrameNumber};
+    mPreviousReleaseBufferEndpoint = mBufferInfo.mReleaseBufferEndpoint;
+    if (!mDrawingState.buffer) {
+        mBufferInfo = {};
+        return;
+    }
+
+    if ((!mBufferInfo.mBuffer || !mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer))) {
+        decrementPendingBufferCount();
+    }
+
+    mBufferInfo.mBuffer = mDrawingState.buffer;
+    mBufferInfo.mReleaseBufferEndpoint = mDrawingState.releaseBufferEndpoint;
+    mBufferInfo.mFence = mDrawingState.acquireFence;
+    mBufferInfo.mFrameNumber = mDrawingState.frameNumber;
+    mBufferInfo.mPixelFormat =
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->getPixelFormat();
+    mBufferInfo.mFrameLatencyNeeded = true;
+    mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
+    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
+    mBufferInfo.mFence = mDrawingState.acquireFence;
+    mBufferInfo.mTransform = mDrawingState.bufferTransform;
+    auto lastDataspace = mBufferInfo.mDataspace;
+    mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
+    if (mBufferInfo.mBuffer != nullptr) {
+        auto& mapper = GraphicBufferMapper::get();
+        // TODO: We should measure if it's faster to do a blind write if we're on newer api levels
+        // and don't need to possibly remaps buffers.
+        ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+        status_t err = OK;
+        {
+            ATRACE_NAME("getDataspace");
+            err = mapper.getDataspace(mBufferInfo.mBuffer->getBuffer()->handle, &dataspace);
+        }
+        if (err != OK || dataspace != mBufferInfo.mDataspace) {
+            {
+                ATRACE_NAME("setDataspace");
+                err = mapper.setDataspace(mBufferInfo.mBuffer->getBuffer()->handle,
+                                          static_cast<ui::Dataspace>(mBufferInfo.mDataspace));
+            }
+
+            // Some GPU drivers may cache gralloc metadata which means before we composite we need
+            // to upsert RenderEngine's caches. Put in a special workaround to be backwards
+            // compatible with old vendors, with a ticking clock.
+            static const int32_t kVendorVersion =
+                    base::GetIntProperty("ro.vndk.version", __ANDROID_API_FUTURE__);
+            if (const auto format =
+                        static_cast<aidl::android::hardware::graphics::common::PixelFormat>(
+                                mBufferInfo.mBuffer->getPixelFormat());
+                err == OK && kVendorVersion < __ANDROID_API_U__ &&
+                (format ==
+                         aidl::android::hardware::graphics::common::PixelFormat::
+                                 IMPLEMENTATION_DEFINED ||
+                 format == aidl::android::hardware::graphics::common::PixelFormat::YCBCR_420_888 ||
+                 format == aidl::android::hardware::graphics::common::PixelFormat::YV12 ||
+                 format == aidl::android::hardware::graphics::common::PixelFormat::YCBCR_P010)) {
+                mBufferInfo.mBuffer->remapBuffer();
+            }
+        }
+    }
+    if (lastDataspace != mBufferInfo.mDataspace) {
+        mFlinger->mHdrLayerInfoChanged = true;
+    }
+    if (mBufferInfo.mDesiredHdrSdrRatio != mDrawingState.desiredHdrSdrRatio) {
+        mBufferInfo.mDesiredHdrSdrRatio = mDrawingState.desiredHdrSdrRatio;
+        mFlinger->mHdrLayerInfoChanged = true;
+    }
+    mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
+    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+    mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
+    mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
+    mBufferInfo.mApi = mDrawingState.api;
+    mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
+}
+
+Rect Layer::computeBufferCrop(const State& s) {
+    if (s.buffer && !s.bufferCrop.isEmpty()) {
+        Rect bufferCrop;
+        s.buffer->getBounds().intersect(s.bufferCrop, &bufferCrop);
+        return bufferCrop;
+    } else if (s.buffer) {
+        return s.buffer->getBounds();
+    } else {
+        return s.bufferCrop;
+    }
+}
+
+sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+    layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
+    return layer;
+}
+
+void Layer::decrementPendingBufferCount() {
+    int32_t pendingBuffers = --mPendingBufferTransactions;
+    tracePendingBufferCount(pendingBuffers);
+}
+
+void Layer::tracePendingBufferCount(int32_t pendingBuffers) {
+    ATRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
+}
+
+/*
+ * We don't want to send the layer's transform to input, but rather the
+ * parent's transform. This is because Layer's transform is
+ * information about how the buffer is placed on screen. The parent's
+ * transform makes more sense to send since it's information about how the
+ * layer is placed on screen. This transform is used by input to determine
+ * how to go from screen space back to window space.
+ */
+ui::Transform Layer::getInputTransform() const {
+    if (!hasBufferOrSidebandStream()) {
+        return getTransform();
+    }
+    sp<Layer> parent = mDrawingParent.promote();
+    if (parent == nullptr) {
+        return ui::Transform();
+    }
+
+    return parent->getTransform();
+}
+
+/**
+ * Returns the bounds used to fill the input frame and the touchable region.
+ *
+ * Similar to getInputTransform, we need to update the bounds to include the transform.
+ * This is because bounds don't include the buffer transform, where the input assumes
+ * that's already included.
+ */
+std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const {
+    Rect croppedBufferSize = getCroppedBufferSize(getDrawingState());
+    FloatRect inputBounds = croppedBufferSize.toFloatRect();
+    if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() &&
+        mDrawingState.transform.getType() != ui::Transform::IDENTITY) {
+        inputBounds = mDrawingState.transform.transform(inputBounds);
+    }
+
+    bool inputBoundsValid = croppedBufferSize.isValid();
+    if (!inputBoundsValid) {
+        /**
+         * Input bounds are based on the layer crop or buffer size. But if we are using
+         * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
+         * we can use the parent bounds as the input bounds if the layer does not have buffer
+         * or a crop. We want to unify this logic but because of compat reasons we cannot always
+         * use the parent bounds. A layer without a buffer can get input. So when a window is
+         * initially added, its touchable region can fill its parent layer bounds and that can
+         * have negative consequences.
+         */
+        inputBounds = fillParentBounds ? mBounds : FloatRect{};
+    }
+
+    // Clamp surface inset to the input bounds.
+    const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset);
+    const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
+    const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
+
+    // Apply the insets to the input bounds.
+    inputBounds.left += xSurfaceInset;
+    inputBounds.top += ySurfaceInset;
+    inputBounds.right -= xSurfaceInset;
+    inputBounds.bottom -= ySurfaceInset;
+
+    return {inputBounds, inputBoundsValid};
+}
+
+bool Layer::simpleBufferUpdate(const layer_state_t& s) const {
+    const uint64_t requiredFlags = layer_state_t::eBufferChanged;
+
+    const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
+            layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
+            layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
+            layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
+            layer_state_t::eReparent;
+
+    const uint64_t allowedFlags = layer_state_t::eHasListenerCallbacksChanged |
+            layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFrameRateChanged |
+            layer_state_t::eSurfaceDamageRegionChanged | layer_state_t::eApiChanged |
+            layer_state_t::eMetadataChanged | layer_state_t::eDropInputModeChanged |
+            layer_state_t::eInputInfoChanged;
+
+    if ((s.what & requiredFlags) != requiredFlags) {
+        ALOGV("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
+              (s.what | requiredFlags) & ~s.what);
+        return false;
+    }
+
+    if (s.what & deniedFlags) {
+        ALOGV("%s: false [has denied flags 0x%" PRIx64 "]", __func__, s.what & deniedFlags);
+        return false;
+    }
+
+    if (s.what & allowedFlags) {
+        ALOGV("%s: [has allowed flags 0x%" PRIx64 "]", __func__, s.what & allowedFlags);
+    }
+
+    if (s.what & layer_state_t::ePositionChanged) {
+        if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
+            ALOGV("%s: false [ePositionChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eAlphaChanged) {
+        if (mDrawingState.color.a != s.color.a) {
+            ALOGV("%s: false [eAlphaChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eColorTransformChanged) {
+        if (mDrawingState.colorTransform != s.colorTransform) {
+            ALOGV("%s: false [eColorTransformChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBackgroundColorChanged) {
+        if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
+            ALOGV("%s: false [eBackgroundColorChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eMatrixChanged) {
+        if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
+            mRequestedTransform.dtdy() != s.matrix.dtdy ||
+            mRequestedTransform.dtdx() != s.matrix.dtdx ||
+            mRequestedTransform.dsdy() != s.matrix.dsdy) {
+            ALOGV("%s: false [eMatrixChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eCornerRadiusChanged) {
+        if (mDrawingState.cornerRadius != s.cornerRadius) {
+            ALOGV("%s: false [eCornerRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
+            ALOGV("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBufferTransformChanged) {
+        if (mDrawingState.bufferTransform != s.bufferTransform) {
+            ALOGV("%s: false [eBufferTransformChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
+        if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
+            ALOGV("%s: false [eTransformToDisplayInverseChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eCropChanged) {
+        if (mDrawingState.crop != s.crop) {
+            ALOGV("%s: false [eCropChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDataspaceChanged) {
+        if (mDrawingState.dataspace != s.dataspace) {
+            ALOGV("%s: false [eDataspaceChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eHdrMetadataChanged) {
+        if (mDrawingState.hdrMetadata != s.hdrMetadata) {
+            ALOGV("%s: false [eHdrMetadataChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eSidebandStreamChanged) {
+        if (mDrawingState.sidebandStream != s.sidebandStream) {
+            ALOGV("%s: false [eSidebandStreamChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
+        if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
+            ALOGV("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eShadowRadiusChanged) {
+        if (mDrawingState.shadowRadius != s.shadowRadius) {
+            ALOGV("%s: false [eShadowRadiusChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eFixedTransformHintChanged) {
+        if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
+            ALOGV("%s: false [eFixedTransformHintChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eTrustedOverlayChanged) {
+        if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
+            ALOGV("%s: false [eTrustedOverlayChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eStretchChanged) {
+        StretchEffect temp = s.stretchEffect;
+        temp.sanitize();
+        if (mDrawingState.stretchEffect != temp) {
+            ALOGV("%s: false [eStretchChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eBufferCropChanged) {
+        if (mDrawingState.bufferCrop != s.bufferCrop) {
+            ALOGV("%s: false [eBufferCropChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDestinationFrameChanged) {
+        if (mDrawingState.destinationFrame != s.destinationFrame) {
+            ALOGV("%s: false [eDestinationFrameChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eDimmingEnabledChanged) {
+        if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
+            ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+        if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio ||
+            mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
+            ALOGV("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
+            return false;
+        }
+    }
+
+    ALOGV("%s: true", __func__);
+    return true;
+}
+
+bool Layer::isHdrY410() const {
+    // pixel format is HDR Y410 masquerading as RGBA_1010102
+    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
+}
+
+sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
+    // There's no need to get a CE Layer if the layer isn't going to draw anything.
+    return hasSomethingToDraw() ? mLegacyLayerFE : nullptr;
+}
+
+const LayerSnapshot* Layer::getLayerSnapshot() const {
+    return mSnapshot.get();
+}
+
+LayerSnapshot* Layer::editLayerSnapshot() {
+    return mSnapshot.get();
+}
+
+std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() {
+    return std::move(mSnapshot);
+}
+
+void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) {
+    mSnapshot = std::move(snapshot);
+}
+
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+    return mSnapshot.get();
+}
+
+sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
+    auto result = mFlinger->getFactory().createLayerFE(mName);
+    result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
+    return result;
+}
+
+sp<LayerFE> Layer::getCompositionEngineLayerFE(
+        const frontend::LayerHierarchy::TraversalPath& path) {
+    for (auto& [p, layerFE] : mLayerFEs) {
+        if (p == path) {
+            return layerFE;
+        }
+    }
+    auto layerFE = mFlinger->getFactory().createLayerFE(mName);
+    mLayerFEs.emplace_back(path, layerFE);
+    return layerFE;
+}
+
+void Layer::useSurfaceDamage() {
+    if (mFlinger->mForceFullDamage) {
+        surfaceDamageRegion = Region::INVALID_REGION;
+    } else {
+        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
+    }
+}
+
+void Layer::useEmptyDamage() {
+    surfaceDamageRegion.clear();
+}
+
+bool Layer::isOpaque(const Layer::State& s) const {
+    // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
+    // layer's opaque flag.
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    // if the layer has the opaque flag, then we're always opaque
+    if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) {
+        return true;
+    }
+
+    // If the buffer has no alpha channel, then we are opaque
+    if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) {
+        return true;
+    }
+
+    // Lastly consider the layer opaque if drawing a color with alpha == 1.0
+    return fillsColor() && getAlpha() == 1.0_hf;
+}
+
+bool Layer::canReceiveInput() const {
+    return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
+bool Layer::isVisible() const {
+    if (!hasSomethingToDraw()) {
+        return false;
+    }
+
+    if (isHiddenByPolicy()) {
+        return false;
+    }
+
+    return getAlpha() > 0.0f || hasBlur();
+}
+
+void Layer::onPostComposition(const DisplayDevice* display,
+                              const std::shared_ptr<FenceTime>& glDoneFence,
+                              const std::shared_ptr<FenceTime>& presentFence,
+                              const CompositorTiming& compositorTiming) {
+    // mFrameLatencyNeeded is true when a new frame was latched for the
+    // composition.
+    if (!mBufferInfo.mFrameLatencyNeeded) return;
+
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->gpuCompositionDoneFence = glDoneFence;
+        handle->compositorTiming = compositorTiming;
+    }
+
+    // Update mFrameTracker.
+    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
+    mFrameTracker.setDesiredPresentTime(desiredPresentTime);
+
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer && outputLayer->requiresClientComposition()) {
+        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               clientCompositionTimestamp,
+                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+        // Update the SurfaceFrames in the drawing state
+        if (mDrawingState.bufferSurfaceFrameTX) {
+            mDrawingState.bufferSurfaceFrameTX->setGpuComposition();
+        }
+        for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
+            surfaceFrame->setGpuComposition();
+        }
+    }
+
+    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
+    if (frameReadyFence->isValid()) {
+        mFrameTracker.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);
+    }
+
+    if (display) {
+        const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
+        const std::optional<Fps> renderRate =
+                mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+        const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+        const auto gameMode = getGameMode();
+
+        if (presentFence->isValid()) {
+            mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
+                                                  refreshRate, renderRate, vote, gameMode);
+            mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               presentFence,
+                                               FrameTracer::FrameEvent::PRESENT_FENCE);
+            mFrameTracker.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.
+            const nsecs_t presentTimestamp =
+                    mFlinger->getHwComposer().getPresentTimestamp(*displayId);
+
+            const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+            const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+            const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
+
+            mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
+                                                 refreshRate, renderRate, vote, gameMode);
+            mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
+                                                   mCurrentFrameNumber, actualPresentTime,
+                                                   FrameTracer::FrameEvent::PRESENT_FENCE);
+            mFrameTracker.setActualPresentTime(actualPresentTime);
+        }
+    }
+
+    mFrameTracker.advanceFrame();
+    mBufferInfo.mFrameLatencyNeeded = false;
+}
+
+bool Layer::willReleaseBufferOnLatch() const {
+    return !mDrawingState.buffer && mBufferInfo.mBuffer;
+}
+
+bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+    const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
+    return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
+}
+
+bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
+    ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
+                          getDrawingState().frameNumber);
+
+    bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
+
+    if (refreshRequired) {
+        return refreshRequired;
+    }
+
+    // If the head buffer's acquire fence hasn't signaled yet, return and
+    // try again later
+    if (!fenceHasSignaled()) {
+        ATRACE_NAME("!fenceHasSignaled()");
+        mFlinger->onLayerUpdate();
+        return false;
+    }
+    updateTexImage(latchTime, bgColorOnly);
+
+    // Capture the old state of the layer for comparisons later
+    BufferInfo oldBufferInfo = mBufferInfo;
+    const bool oldOpacity = isOpaque(mDrawingState);
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mDrawingState.frameNumber;
+    gatherBufferInfo();
+
+    if (mBufferInfo.mBuffer) {
+        // We latched a buffer that will be presented soon. Clear the previously presented layer
+        // stack list.
+        mPreviouslyPresentedLayerStacks.clear();
+    }
+
+    if (mDrawingState.buffer == nullptr) {
+        const bool bufferReleased = oldBufferInfo.mBuffer != nullptr;
+        recomputeVisibleRegions = bufferReleased;
+        return bufferReleased;
+    }
+
+    if (oldBufferInfo.mBuffer == nullptr) {
+        // the first time we receive a buffer, we need to trigger a
+        // geometry invalidation.
+        recomputeVisibleRegions = true;
+    }
+
+    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
+        recomputeVisibleRegions = true;
+    }
+
+    if (oldBufferInfo.mBuffer != nullptr) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+        if (bufWidth != oldBufferInfo.mBuffer->getWidth() ||
+            bufHeight != oldBufferInfo.mBuffer->getHeight()) {
+            recomputeVisibleRegions = true;
+        }
+    }
+
+    if (oldOpacity != isOpaque(mDrawingState)) {
+        recomputeVisibleRegions = true;
     }
 
     return true;
 }
 
+bool Layer::hasReadyFrame() const {
+    return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
+}
+
+bool Layer::isProtected() const {
+    return (mBufferInfo.mBuffer != nullptr) &&
+            (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+}
+
+void Layer::latchAndReleaseBuffer() {
+    if (hasReadyFrame()) {
+        bool ignored = false;
+        latchBuffer(ignored, systemTime());
+    }
+    releasePendingBuffer(systemTime());
+}
+
+PixelFormat Layer::getPixelFormat() const {
+    return mBufferInfo.mPixelFormat;
+}
+
+bool Layer::getTransformToDisplayInverse() const {
+    return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect Layer::getBufferCrop() const {
+    // this is the crop rectangle that applies to the buffer
+    // itself (as opposed to the window)
+    if (!mBufferInfo.mCrop.isEmpty()) {
+        // if the buffer crop is defined, we use that
+        return mBufferInfo.mCrop;
+    } else if (mBufferInfo.mBuffer != nullptr) {
+        // otherwise we use the whole buffer
+        return mBufferInfo.mBuffer->getBounds();
+    } else {
+        // if we don't have a buffer yet, we use an empty/invalid crop
+        return Rect();
+    }
+}
+
+uint32_t Layer::getBufferTransform() const {
+    return mBufferInfo.mTransform;
+}
+
+ui::Dataspace Layer::getDataSpace() const {
+    return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
+}
+
+ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
+    ui::Dataspace updatedDataspace = dataspace;
+    // translate legacy dataspaces to modern dataspaces
+    switch (dataspace) {
+        // Treat unknown dataspaces as V0_sRGB
+        case ui::Dataspace::UNKNOWN:
+        case ui::Dataspace::SRGB:
+            updatedDataspace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::Dataspace::SRGB_LINEAR:
+            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+            break;
+        case ui::Dataspace::JFIF:
+            updatedDataspace = ui::Dataspace::V0_JFIF;
+            break;
+        case ui::Dataspace::BT601_625:
+            updatedDataspace = ui::Dataspace::V0_BT601_625;
+            break;
+        case ui::Dataspace::BT601_525:
+            updatedDataspace = ui::Dataspace::V0_BT601_525;
+            break;
+        case ui::Dataspace::BT709:
+            updatedDataspace = ui::Dataspace::V0_BT709;
+            break;
+        default:
+            break;
+    }
+
+    return updatedDataspace;
+}
+
+sp<GraphicBuffer> Layer::getBuffer() const {
+    return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
+}
+
+void Layer::setTransformHintLegacy(ui::Transform::RotationFlags displayTransformHint) {
+    mTransformHintLegacy = getFixedTransformHint();
+    if (mTransformHintLegacy == ui::Transform::ROT_INVALID) {
+        mTransformHintLegacy = displayTransformHint;
+    }
+    mSkipReportingTransformHint = false;
+}
+
+const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
+    return mBufferInfo.mBuffer;
+}
+
+bool Layer::setColor(const half3& color) {
+    if (mDrawingState.color.rgb == color) {
+        return false;
+    }
+
+    mDrawingState.sequence++;
+    mDrawingState.color.rgb = color;
+    mDrawingState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::fillsColor() const {
+    return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf &&
+            mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf;
+}
+
+bool Layer::hasBlur() const {
+    return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
+}
+
+void Layer::updateSnapshot(bool updateGeometry) {
+    if (!getCompositionEngineLayerFE()) {
+        return;
+    }
+
+    auto* snapshot = editLayerSnapshot();
+    if (updateGeometry) {
+        prepareBasicGeometryCompositionState();
+        prepareGeometryCompositionState();
+        snapshot->roundedCorner = getRoundedCornerState();
+        snapshot->stretchEffect = getStretchEffect();
+        snapshot->transformedBounds = mScreenBounds;
+        if (mEffectiveShadowRadius > 0.f) {
+            snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
+
+            // Note: this preserves existing behavior of shadowing the entire layer and not cropping
+            // it if transparent regions are present. This may not be necessary since shadows are
+            // typically cast by layers without transparent regions.
+            snapshot->shadowSettings.boundaries = mBounds;
+
+            const float casterAlpha = snapshot->alpha;
+            const bool casterIsOpaque =
+                    ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState));
+
+            // If the casting layer is translucent, we need to fill in the shadow underneath the
+            // layer. Otherwise the generated shadow will only be shown around the casting layer.
+            snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+            snapshot->shadowSettings.ambientColor *= casterAlpha;
+            snapshot->shadowSettings.spotColor *= casterAlpha;
+        }
+        snapshot->shadowSettings.length = mEffectiveShadowRadius;
+    }
+    snapshot->contentOpaque = isOpaque(mDrawingState);
+    snapshot->layerOpaqueFlagSet =
+            (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
+    snapshot->isHdrY410 = isHdrY410();
+    sp<Layer> p = mDrawingParent.promote();
+    if (p != nullptr) {
+        snapshot->parentTransform = p->getTransform();
+    } else {
+        snapshot->parentTransform.reset();
+    }
+    snapshot->bufferSize = getBufferSize(mDrawingState);
+    snapshot->externalTexture = mBufferInfo.mBuffer;
+    snapshot->hasReadyFrame = hasReadyFrame();
+    preparePerFrameCompositionState();
+}
+
+void Layer::updateChildrenSnapshots(bool updateGeometry) {
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->updateSnapshot(updateGeometry);
+        child->updateChildrenSnapshots(updateGeometry);
+    }
+}
+
+void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
+    mSnapshot->layerMetadata = parentMetadata;
+    mSnapshot->layerMetadata.merge(mDrawingState.metadata);
+    for (const sp<Layer>& child : mDrawingChildren) {
+        child->updateMetadataSnapshot(mSnapshot->layerMetadata);
+    }
+}
+
+void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                           std::unordered_set<Layer*>& visited) {
+    if (visited.find(this) != visited.end()) {
+        ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
+        return;
+    }
+    visited.insert(this);
+
+    mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
+
+    if (mDrawingState.zOrderRelatives.empty()) {
+        return;
+    }
+    LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
+    childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
+    for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
+        sp<Layer> relative = weakRelative.promote();
+        if (!relative) {
+            continue;
+        }
+        relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
+    }
+}
+
+bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+                                       TrustedPresentationListener const& listener) {
+    bool hadTrustedPresentationListener = hasTrustedPresentationListener();
+    mTrustedPresentationListener = listener;
+    mTrustedPresentationThresholds = thresholds;
+    bool haveTrustedPresentationListener = hasTrustedPresentationListener();
+    if (!hadTrustedPresentationListener && haveTrustedPresentationListener) {
+        mFlinger->mNumTrustedPresentationListeners++;
+    } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) {
+        mFlinger->mNumTrustedPresentationListeners--;
+    }
+
+    // Reset trusted presentation states to ensure we start the time again.
+    mEnteredTrustedPresentationStateTime = -1;
+    mLastReportedTrustedPresentationState = false;
+    mLastComputedTrustedPresentationState = false;
+
+    // If there's a new trusted presentation listener, the code needs to go through the composite
+    // path to ensure it recomutes the current state and invokes the TrustedPresentationListener if
+    // we're already in the requested state.
+    return haveTrustedPresentationListener;
+}
+
+void Layer::updateLastLatchTime(nsecs_t latchTime) {
+    mLastLatchTime = latchTime;
+}
+
 // ---------------------------------------------------------------------------
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5ffcabf..f7596e2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -17,8 +17,8 @@
 #pragma once
 
 #include <android/gui/DropInputMode.h>
+#include <android/gui/ISurfaceComposerClient.h>
 #include <gui/BufferQueue.h>
-#include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerState.h>
 #include <gui/WindowInfo.h>
 #include <layerproto/LayerProtoHeader.h>
@@ -38,6 +38,7 @@
 #include <utils/Timers.h>
 
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <scheduler/Fps.h>
 #include <scheduler/Seamlessness.h>
 
@@ -48,13 +49,10 @@
 #include <vector>
 
 #include "Client.h"
-#include "ClientCache.h"
-#include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/HWComposer.h"
 #include "FrameTracker.h"
+#include "LayerFE.h"
 #include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "RenderArea.h"
 #include "Scheduler/LayerInfo.h"
 #include "SurfaceFlinger.h"
 #include "Tracing/LayerTracing.h"
@@ -69,39 +67,22 @@
 class DisplayDevice;
 class GraphicBuffer;
 class SurfaceFlinger;
-class LayerDebugInfo;
 
 namespace compositionengine {
 class OutputLayer;
 struct LayerFECompositionState;
 }
 
-namespace impl {
-class SurfaceInterceptor;
+namespace gui {
+class LayerDebugInfo;
 }
 
 namespace frametimeline {
 class SurfaceFrame;
 } // namespace frametimeline
 
-struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
-
-    SurfaceFlinger* flinger;
-    const sp<Client> client;
-    std::string name;
-    uint32_t flags;
-    LayerMetadata metadata;
-
-    pid_t callingPid;
-    uid_t callingUid;
-    uint32_t textureName;
-    std::optional<uint32_t> sequence = std::nullopt;
-    bool addToRoot = true;
-};
-
-class Layer : public virtual RefBase, compositionengine::LayerFE {
-    static std::atomic<int32_t> sSequence;
+class Layer : public virtual RefBase {
+public:
     // The following constants represent priority of the window. SF uses this information when
     // deciding which window has a priority when deciding about the refresh rate of the screen.
     // Priority 0 is considered the highest priority. -1 means that the priority is unset.
@@ -113,7 +94,6 @@
     // Windows that are not in focus, but voted for a specific mode ID.
     static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
 
-public:
     enum { // flags for doTransaction()
         eDontUpdateGeometryState = 0x00000001,
         eVisibleRegion = 0x00000002,
@@ -132,85 +112,55 @@
         inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
     };
 
-    struct RoundedCornerState {
-        RoundedCornerState() = default;
-        RoundedCornerState(const FloatRect& cropRect, const vec2& radius)
-              : cropRect(cropRect), radius(radius) {}
-
-        // Rounded rectangle in local layer coordinate space.
-        FloatRect cropRect = FloatRect();
-        // Radius of the rounded rectangle.
-        vec2 radius;
-        bool hasRoundedCorners() const { return radius.x > 0.0f && radius.y > 0.0f; }
-    };
-
     using FrameRate = scheduler::LayerInfo::FrameRate;
     using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
 
     struct State {
-        Geometry active_legacy;
-        Geometry requested_legacy;
         int32_t z;
-
         ui::LayerStack layerStack;
-
         uint32_t flags;
-        uint8_t reserved[2];
         int32_t sequence; // changes when visible regions can change
         bool modified;
-
         // Crop is expressed in layer space coordinate.
         Rect crop;
-        Rect requestedCrop;
-
-        // the transparentRegion hint is a bit special, it's latched only
-        // when we receive a buffer -- this is because it's "content"
-        // dependent.
-        Region activeTransparentRegion_legacy;
-        Region requestedTransparentRegion_legacy;
-
         LayerMetadata metadata;
-
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
         bool isRelativeOf{false};
 
         // A list of surfaces whose Z-order is interpreted relative to ours.
         SortedVector<wp<Layer>> zOrderRelatives;
-
         half4 color;
         float cornerRadius;
         int backgroundBlurRadius;
-
         gui::WindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
-        // dataspace is only used by BufferStateLayer and EffectLayer
         ui::Dataspace dataspace;
 
-        // The fields below this point are only used by BufferStateLayer
         uint64_t frameNumber;
-        uint32_t width;
-        uint32_t height;
+        // high watermark framenumber to use to check for barriers to protect ourselves
+        // from out of order transactions
+        uint64_t barrierFrameNumber;
         ui::Transform transform;
 
+        uint32_t producerId = 0;
+        // high watermark producerId to use to check for barriers to protect ourselves
+        // from out of order transactions
+        uint32_t barrierProducerId = 0;
+
         uint32_t bufferTransform;
         bool transformToDisplayInverse;
-
         Region transparentRegionHint;
-
         std::shared_ptr<renderengine::ExternalTexture> buffer;
-        client_cache_t clientCacheId;
         sp<Fence> acquireFence;
         std::shared_ptr<FenceTime> acquireFenceTime;
         HdrMetadata hdrMetadata;
         Region surfaceDamageRegion;
         int32_t api;
-
         sp<NativeHandle> sidebandStream;
         mat4 colorTransform;
         bool hasColorTransform;
-
         // pointer to background color layer that, if set, appears below the buffer state layer
         // and the buffer state layer's children.  Z order will be set to
         // INT_MIN
@@ -233,6 +183,8 @@
         // Priority of the layer assigned by Window Manager.
         int32_t frameRateSelectionPriority;
 
+        // Default frame rate compatibility used to set the layer refresh rate votetype.
+        FrameRateCompatibility defaultFrameRateCompatibility;
         FrameRate frameRate;
 
         // The combined frame rate of parents / children of this layer
@@ -252,7 +204,6 @@
 
         // When the transaction was posted
         nsecs_t postTime;
-
         sp<ITransactionCompletedListener> releaseBufferListener;
         // SurfaceFrame that tracks the timeline of Transactions that contain a Buffer. Only one
         // such SurfaceFrame exists because only one buffer can be presented on the layer per vsync.
@@ -273,59 +224,19 @@
 
         // Whether or not this layer is a trusted overlay for input
         bool isTrustedOverlay;
-
         Rect bufferCrop;
         Rect destinationFrame;
-
         sp<IBinder> releaseBufferEndpoint;
-
         gui::DropInputMode dropInputMode;
-
         bool autoRefresh = false;
-
         bool dimmingEnabled = true;
+        float currentHdrSdrRatio = 1.f;
+        float desiredHdrSdrRatio = 1.f;
+        gui::CachingHint cachingHint = gui::CachingHint::Enabled;
+        int64_t latchedVsyncId = 0;
+        bool useVsyncIdForRefreshRateSelection = false;
     };
 
-    /*
-     * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer)
-     * is called.
-     */
-    class LayerCleaner {
-        sp<SurfaceFlinger> mFlinger;
-        sp<Layer> mLayer;
-        BBinder* mHandle;
-
-    protected:
-        ~LayerCleaner() {
-            // destroy client resources
-            mFlinger->onHandleDestroyed(mHandle, mLayer);
-        }
-
-    public:
-        LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle)
-              : mFlinger(flinger), mLayer(layer), mHandle(handle) {}
-    };
-
-    /*
-     * The layer handle is just a BBinder object passed to the client
-     * (remote process) -- we don't keep any reference on our side such that
-     * the dtor is called when the remote side let go of its reference.
-     *
-     * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
-     * this layer when the handle is destroyed.
-     */
-    class Handle : public BBinder, public LayerCleaner {
-    public:
-        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
-              : LayerCleaner(flinger, layer, this), owner(layer) {}
-        const String16& getInterfaceDescriptor() const override { return kDescriptor; }
-
-        static const String16 kDescriptor;
-        wp<Layer> owner;
-    };
-
-    static wp<Layer> fromHandle(const sp<IBinder>& handle);
-
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
@@ -333,43 +244,17 @@
     static void miniDumpHeader(std::string& result);
 
     // Provide unique string for each class type in the Layer hierarchy
-    virtual const char* getType() const = 0;
+    virtual const char* getType() const { return "Layer"; }
 
     // true if this layer is visible, false otherwise
-    virtual bool isVisible() const = 0;
+    virtual bool isVisible() const;
 
-    virtual sp<Layer> createClone() = 0;
+    virtual sp<Layer> createClone(uint32_t mirrorRoot);
 
-    // Geometry setting functions.
-    //
-    // The following group of functions are used to specify the layers
-    // bounds, and the mapping of the texture on to those bounds. According
-    // to various settings changes to them may apply immediately, or be delayed until
-    // a pending resize is completed by the producer submitting a buffer. For example
-    // if we were to change the buffer size, and update the matrix ahead of the
-    // new buffer arriving, then we would be stretching the buffer to a different
-    // aspect before and after the buffer arriving, which probably isn't what we wanted.
-    //
-    // The first set of geometry functions are controlled by the scaling mode, described
-    // in window.h. The scaling mode may be set by the client, as it submits buffers.
-    //
-    // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
-    // matrix updates will not be applied while a resize is pending
-    // and the size and transform will remain in their previous state
-    // until a new buffer is submitted. If the scaling mode is another value
-    // then the old-buffer will immediately be scaled to the pending size
-    // and the new matrix will be immediately applied following this scaling
-    // transformation.
-
-    // Set the default buffer size for the assosciated Producer, in pixels. This is
-    // also the rendered size of the layer prior to any transformations. Parent
-    // or local matrix transformations will not affect the size of the buffer,
-    // but may affect it's on-screen size or clipping.
-    virtual bool setSize(uint32_t w, uint32_t h);
     // Set a 2x2 transformation matrix on the layer. This transform
     // will be applied after parent transforms, but before any final
     // producer specified transform.
-    virtual bool setMatrix(const layer_state_t::matrix22_t& matrix);
+    bool setMatrix(const layer_state_t::matrix22_t& matrix);
 
     // This second set of geometry attributes are controlled by
     // setGeometryAppliesWithResize, and their default mode is to be
@@ -379,9 +264,9 @@
 
     // setPosition operates in parent buffer space (pre parent-transform) or display
     // space for top-level layers.
-    virtual bool setPosition(float x, float y);
+    bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop(const Rect& crop);
+    bool setCrop(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -390,7 +275,7 @@
     virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
 
     virtual bool setAlpha(float alpha);
-    virtual bool setColor(const half3& /*color*/) { return false; };
+    bool setColor(const half3& /*color*/);
 
     // Set rounded corner radius for this layer and its children.
     //
@@ -402,11 +287,13 @@
     // is specified in pixels.
     virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
     virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
-    virtual bool setTransparentRegionHint(const Region& transparent);
+    bool setTransparentRegionHint(const Region& transparent);
     virtual bool setTrustedOverlay(bool);
     virtual bool setFlags(uint32_t flags, uint32_t mask);
     virtual bool setLayerStack(ui::LayerStack);
-    virtual ui::LayerStack getLayerStack() const;
+    virtual ui::LayerStack getLayerStack(
+            LayerVector::StateSet state = LayerVector::StateSet::Drawing) const;
+
     virtual bool setMetadata(const LayerMetadata& data);
     virtual void setChildrenDrawingParent(const sp<Layer>&);
     virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock);
@@ -414,48 +301,61 @@
     virtual mat4 getColorTransform() const;
     virtual bool hasColorTransform() const;
     virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
-    virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; };
+    virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
+    float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; }
+    float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; }
+    gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
 
-    // Used only to set BufferStateLayer state
-    virtual bool setTransform(uint32_t /*transform*/) { return false; };
-    virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
-    virtual bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
-                           const BufferData& /* bufferData */, nsecs_t /* postTime */,
-                           nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
-                           std::optional<nsecs_t> /* dequeueTime */,
-                           const FrameTimelineInfo& /*info*/) {
-        return false;
-    };
-    virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
-    virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
-    virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
-    virtual bool setApi(int32_t /*api*/) { return false; };
-    virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
-    virtual bool setTransactionCompletedListeners(
-            const std::vector<sp<CallbackHandle>>& /*handles*/);
+    bool setTransform(uint32_t /*transform*/);
+    bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
+    bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
+                   const BufferData& /* bufferData */, nsecs_t /* postTime */,
+                   nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+                   std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+    void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
+    bool setDataspace(ui::Dataspace /*dataspace*/);
+    bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
+    bool setCachingHint(gui::CachingHint cachingHint);
+    bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
+    bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
+    bool setApi(int32_t /*api*/);
+    bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/);
+    bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
+                                          bool willPresent);
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
             REQUIRES(mFlinger->mStateLock);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
     virtual bool setDimmingEnabled(const bool dimmingEnabled);
+    virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility);
     virtual bool setFrameRateSelectionPriority(int32_t priority);
     virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
-    virtual void setAutoRefresh(bool /* autoRefresh */) {}
+    void setAutoRefresh(bool /* autoRefresh */);
     bool setDropInputMode(gui::DropInputMode);
 
     //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
     //  rate priority from its parent.
     virtual int32_t getFrameRateSelectionPriority() const;
-    virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
+    //
+    virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const;
+    //
+    ui::Dataspace getDataSpace() const;
 
-    virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
-    virtual compositionengine::LayerFECompositionState* editCompositionState();
+    virtual sp<LayerFE> getCompositionEngineLayerFE() const;
+    virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
+    sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
+
+    const frontend::LayerSnapshot* getLayerSnapshot() const;
+    frontend::LayerSnapshot* editLayerSnapshot();
+    std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot();
+    void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot);
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
     // one empty rect.
-    virtual void useSurfaceDamage() {}
-    virtual void useEmptyDamage() {}
+    void useSurfaceDamage();
+    void useEmptyDamage();
     Region getVisibleRegion(const DisplayDevice*) const;
+    void updateLastLatchTime(nsecs_t latchtime);
 
     /*
      * isOpaque - true if this surface is opaque
@@ -464,12 +364,12 @@
      * pixel format includes an alpha channel) and the "opaque" flag set
      * on the layer.  It does not examine the current plane alpha value.
      */
-    virtual bool isOpaque(const Layer::State&) const { return false; }
+    bool isOpaque(const Layer::State&) const;
 
     /*
      * Returns whether this layer can receive input.
      */
-    virtual bool canReceiveInput() const;
+    bool canReceiveInput() const;
 
     /*
      * Whether or not the layer should be considered visible for input calculations.
@@ -490,7 +390,7 @@
      * isProtected - true if the layer may contain protected contents in the
      * GRALLOC_USAGE_PROTECTED sense.
      */
-    virtual bool isProtected() const { return false; }
+    bool isProtected() const;
 
     /*
      * isFixedSize - true if content has a fixed size
@@ -500,21 +400,19 @@
     /*
      * usesSourceCrop - true if content should use a source crop
      */
-    virtual bool usesSourceCrop() const { return false; }
+    bool usesSourceCrop() const { return hasBufferOrSidebandStream(); }
 
     // Most layers aren't created from the main thread, and therefore need to
     // grab the SF state lock to access HWC, but ContainerLayer does, so we need
     // to avoid grabbing the lock again to avoid deadlock
     virtual bool isCreatedFromMainThread() const { return false; }
 
-    uint32_t getActiveWidth(const Layer::State& s) const { return s.width; }
-    uint32_t getActiveHeight(const Layer::State& s) const { return s.height; }
     ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
-    virtual Region getActiveTransparentRegion(const Layer::State& s) const {
-        return s.activeTransparentRegion_legacy;
+    Region getActiveTransparentRegion(const Layer::State& s) const {
+        return s.transparentRegionHint;
     }
-    virtual Rect getCrop(const Layer::State& s) const { return s.crop; }
-    virtual bool needsFiltering(const DisplayDevice*) const { return false; }
+    Rect getCrop(const Layer::State& s) const { return s.crop; }
+    bool needsFiltering(const DisplayDevice*) const;
 
     // True if this layer requires filtering
     // This method is distinct from needsFiltering() in how the filter
@@ -525,32 +423,25 @@
     // different.
     // If the parent transform needs to be undone when capturing the layer, then
     // the inverse parent transform is also required.
-    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
-        return false;
-    }
+    bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
 
-    virtual void updateCloneBufferInfo(){};
+    // from graphics API
+    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+    void updateCloneBufferInfo();
+    uint64_t mPreviousFrameNumber = 0;
 
-    virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
-
-    virtual bool isHdrY410() const { return false; }
-
-    virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
+    bool isHdrY410() const;
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual void onPostComposition(const DisplayDevice*,
-                                   const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                                   const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming&) {}
+    void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                           const std::shared_ptr<FenceTime>& /*presentFence*/,
+                           const CompositorTiming&);
 
     // If a buffer was replaced this frame, release the former buffer
-    virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
-
-    virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
-                                           const CompositorTiming& /*compositorTiming*/) {}
+    void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
 
     /*
      * latchBuffer - called each time the screen is redrawn and returns whether
@@ -558,89 +449,119 @@
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
-                             nsecs_t /*expectedPresentTime*/) {
-        return false;
-    }
+    bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
 
-    virtual void latchAndReleaseBuffer() {}
+    bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                         bool bgColorOnly);
+
+    /*
+     * Returns true if the currently presented buffer will be released when this layer state
+     * is latched. This will return false if there is no buffer currently presented.
+     */
+    bool willReleaseBufferOnLatch() const;
+
+    /*
+     * Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
+     * This is used if the buffer is just latched and releases to free up the buffer
+     * and will not be shown on screen.
+     * Should only be called on the main thread.
+     */
+    void latchAndReleaseBuffer();
 
     /*
      * returns the rectangle that crops the content of the layer and scales it
      * to the layer's size.
      */
-    virtual Rect getBufferCrop() const { return Rect(); }
+    Rect getBufferCrop() const;
 
     /*
      * Returns the transform applied to the buffer.
      */
-    virtual uint32_t getBufferTransform() const { return 0; }
+    uint32_t getBufferTransform() const;
 
-    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
-    virtual const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const {
-        return mDrawingState.buffer;
-    };
-
-    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+    sp<GraphicBuffer> getBuffer() const;
+    const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const;
 
     /*
      * Returns if a frame is ready
      */
-    virtual bool hasReadyFrame() const { return false; }
+    bool hasReadyFrame() const;
 
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
     /**
      * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
-     * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+     * any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the
+     * layer does not have a display frame and its parent is not bounded.
      */
-    virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+    Rect getBufferSize(const Layer::State&) const;
 
     /**
      * Returns the source bounds. If the bounds are not defined, it is inferred from the
      * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
      * For the root layer, this is the display viewport size.
      */
-    virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const {
-        return parentBounds;
-    }
+    FloatRect computeSourceBounds(const FloatRect& parentBounds) const;
     virtual FrameRate getFrameRateForLayerTree() const;
 
-    virtual bool getTransformToDisplayInverse() const { return false; }
+    bool getTransformToDisplayInverse() const;
 
     // Returns how rounded corners should be drawn for this layer.
     // A layer can override its parent's rounded corner settings if the parent's rounded
     // corner crop does not intersect with its own rounded corner crop.
-    virtual RoundedCornerState getRoundedCornerState() const;
+    virtual frontend::RoundedCornerState getRoundedCornerState() const;
 
-    bool hasRoundedCorners() const override { return getRoundedCornerState().hasRoundedCorners(); }
+    bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); }
 
-    virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
+    PixelFormat getPixelFormat() const;
     /**
-     * Return whether this layer needs an input info. For most layer types
-     * this is only true if they explicitly set an input-info but BufferLayer
-     * overrides this so we can generate input-info for Buffered layers that don't
-     * have them (for input occlusion detection checks).
+     * Return whether this layer needs an input info. We generate InputWindowHandles for all
+     * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable
+     * the InputDispatcher to do PID based occlusion detection.
      */
-    virtual bool needsInputInfo() const { return hasInputInfo(); }
+    bool needsInputInfo() const {
+        return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor;
+    }
 
     // Implements RefBase.
     void onFirstRef() override;
 
-    // implements compositionengine::LayerFE
-    const compositionengine::LayerFECompositionState* getCompositionState() const override;
-    bool onPreComposition(nsecs_t) override;
-    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
-    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
-    void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override;
+    struct BufferInfo {
+        nsecs_t mDesiredPresentTime;
+        std::shared_ptr<FenceTime> mFenceTime;
+        sp<Fence> mFence;
+        uint32_t mTransform{0};
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        Rect mCrop;
+        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+        Region mSurfaceDamage;
+        HdrMetadata mHdrMetadata;
+        int mApi;
+        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
+        bool mTransformToDisplayInverse{false};
 
-    void setWasClientComposed(const sp<Fence>& fence) override {
+        std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+        uint64_t mFrameNumber;
+        sp<IBinder> mReleaseBufferEndpoint;
+
+        bool mFrameLatencyNeeded{false};
+        float mDesiredHdrSdrRatio = 1.f;
+    };
+
+    BufferInfo mBufferInfo;
+
+    // implements compositionengine::LayerFE
+    const compositionengine::LayerFECompositionState* getCompositionState() const;
+    bool fenceHasSignaled() const;
+    void onPreComposition(nsecs_t refreshStartTime);
+    void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack);
+
+    void setWasClientComposed(const sp<Fence>& fence) {
         mLastClientCompositionFence = fence;
         mClearClientCompositionFenceOnLayerDisplayed = false;
     }
 
-    const char* getDebugName() const override;
+    const char* getDebugName() const;
 
     bool setShadowRadius(float shadowRadius);
 
@@ -653,6 +574,20 @@
 
     uint32_t getTransactionFlags() const { return mTransactionFlags; }
 
+    static bool computeTrustedPresentationState(const FloatRect& bounds,
+                                                const FloatRect& sourceBounds,
+                                                const Region& coveredRegion,
+                                                const FloatRect& screenBounds, float,
+                                                const ui::Transform&,
+                                                const TrustedPresentationThresholds&);
+    void updateTrustedPresentationState(const DisplayDevice* display,
+                                        const frontend::LayerSnapshot* snapshot, int64_t time_in_ms,
+                                        bool leaveState);
+
+    inline bool hasTrustedPresentationListener() {
+        return mTrustedPresentationListener.callbackInterface != nullptr;
+    }
+
     // Sets the masked bits.
     void setTransactionFlags(uint32_t mask);
 
@@ -661,11 +596,13 @@
 
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
+    Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds,
+                                      const ui::Transform& displayTransform);
 
     // Compute bounds for the layer and cache the results.
     void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
-    int32_t getSequence() const override { return sequence; }
+    int32_t getSequence() const { return sequence; }
 
     // For tracing.
     // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
@@ -701,6 +638,7 @@
     bool isRemovedFromCurrentState() const;
 
     LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags);
+    void writeCompositionStateToProto(LayerProto* layerProto, ui::LayerStack layerStack);
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
@@ -714,7 +652,7 @@
 
     gui::WindowInfo::Type getWindowType() const { return mWindowType; }
 
-    void updateMirrorInfo();
+    bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates);
 
     /*
      * doTransaction - process the transaction. This is a good place to figure
@@ -748,15 +686,15 @@
      * Sets display transform hint on BufferLayerConsumer.
      */
     void updateTransformHint(ui::Transform::RotationFlags);
-
+    void skipReportingTransformHint();
     inline const State& getDrawingState() const { return mDrawingState; }
     inline State& getDrawingState() { return mDrawingState; }
 
-    LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
+    gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
 
     void miniDump(std::string& result, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
-    void dumpCallingUidPid(std::string& result) const;
+    void dumpOffscreenDebugInfo(std::string& result) const;
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
@@ -790,6 +728,7 @@
     void traverse(LayerVector::StateSet, const LayerVector::Visitor&);
     void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
     void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
+    void traverseChildren(const LayerVector::Visitor&);
 
     /**
      * Traverse only children in z order, ignoring relative layers that are not children of the
@@ -797,7 +736,10 @@
      */
     void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&);
 
-    size_t getChildrenCount() const;
+    size_t getDescendantCount() const;
+    size_t getChildrenCount() const { return mDrawingChildren.size(); }
+    bool isHandleAlive() const { return mHandleAlive; }
+    bool onHandleDestroyed() { return mHandleAlive = false; }
 
     // ONLY CALL THIS FROM THE LAYER DTOR!
     // See b/141111965.  We need to add current children to offscreen layers in
@@ -854,6 +796,9 @@
     std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
             const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
 
+    bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+                                    TrustedPresentationListener const& listener);
+
     // Creates a new handle each time, so we only expect
     // this to be called once.
     sp<IBinder> getHandle();
@@ -872,12 +817,12 @@
      */
     bool hasInputInfo() const;
 
-    // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this
-    // GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
-    void setGameModeForTree(GameMode);
+    // Sets the gui::GameMode for the tree rooted at this layer. A layer in the tree inherits this
+    // gui::GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
+    void setGameModeForTree(gui::GameMode);
 
-    void setGameMode(GameMode gameMode) { mGameMode = gameMode; }
-    GameMode getGameMode() const { return mGameMode; }
+    void setGameMode(gui::GameMode gameMode) { mGameMode = gameMode; }
+    gui::GameMode getGameMode() const { return mGameMode; }
 
     virtual uid_t getOwnerUid() const { return mOwnerUid; }
 
@@ -902,25 +847,75 @@
 
     bool mPendingHWCDestroy{false};
 
-    bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; }
+    bool backpressureEnabled() const {
+        return mDrawingState.flags & layer_state_t::eEnableBackpressure;
+    }
 
     bool setStretchEffect(const StretchEffect& effect);
     StretchEffect getStretchEffect() const;
+    bool enableBorder(bool shouldEnable, float width, const half4& color);
+    bool isBorderEnabled();
+    float getBorderWidth();
+    const half4& getBorderColor();
 
-    virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
-    virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
-    virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; }
-    virtual std::string getPendingBufferCounterName() { return ""; }
-    virtual bool updateGeometry() { return false; }
+    bool setBufferCrop(const Rect& /* bufferCrop */);
+    bool setDestinationFrame(const Rect& /* destinationFrame */);
+    // See mPendingBufferTransactions
+    void decrementPendingBufferCount();
+    std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+    std::string getPendingBufferCounterName() { return mBlastTransactionName; }
+    bool updateGeometry();
 
-    virtual bool simpleBufferUpdate(const layer_state_t&) const { return false; }
+    bool simpleBufferUpdate(const layer_state_t&) const;
 
+    static bool isOpaqueFormat(PixelFormat format);
+
+    // Updates the LayerSnapshot. This must be called prior to sending layer data to
+    // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
+    // LayerFE::prepareClientComposition).
+    //
+    // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
+    // CompositionEngine to create a single path for composing layers.
+    void updateSnapshot(bool updateGeometry);
+    void updateChildrenSnapshots(bool updateGeometry);
+    void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
+    void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
+                                        std::unordered_set<Layer*>& visited);
+    sp<Layer> getClonedFrom() const {
+        return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr;
+    }
+    bool isClone() { return mClonedFrom != nullptr; }
+
+    bool willPresentCurrentTransaction() const;
+
+    void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                                   const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                                   const sp<Fence>& releaseFence);
+    bool setFrameRateForLayerTreeLegacy(FrameRate);
+    bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
+    void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
+    void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+    auto getLayerProps() const {
+        return scheduler::LayerProps{
+                .visible = isVisible(),
+                .bounds = getBounds(),
+                .transform = getTransform(),
+                .setFrameRateVote = getFrameRateForLayerTree(),
+                .frameRateSelectionPriority = getFrameRateSelectionPriority(),
+        };
+    };
+    bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
+    void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
+        mTransformHint = transformHint;
+    }
+    // Keeps track of the previously presented layer stacks. This is used to get
+    // the release fences from the correct displays when we release the last buffer
+    // from the layer.
+    std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
     // Exposed so SurfaceFlinger can assert that it's held
     const sp<SurfaceFlinger> mFlinger;
 
 protected:
-    friend class impl::SurfaceInterceptor;
-
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class FpsReporterTest;
@@ -929,21 +924,14 @@
     friend class TransactionFrameTracerTest;
     friend class TransactionSurfaceFrameTest;
 
-    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
-    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
-            compositionengine::LayerFE::ClientCompositionTargetSettings&);
-    virtual void preparePerFrameCompositionState();
+    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId);
+    void preparePerFrameCompositionState();
+    void preparePerFrameBufferCompositionState();
+    void preparePerFrameEffectsCompositionState();
     virtual void commitTransaction(State& stateToCommit);
-    virtual void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&) {}
+    void gatherBufferInfo();
+    void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
 
-    // Returns mCurrentScaling mode (originating from the
-    // Client) or mOverrideScalingMode mode (originating from
-    // the Surface Controller) if set.
-    virtual uint32_t getEffectiveScalingMode() const { return 0; }
-
-    sp<compositionengine::LayerFE> asLayerFE() const;
-    sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
-    bool isClone() { return mClonedFrom != nullptr; }
     bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
 
     void cloneDrawingState(const Layer* from);
@@ -954,11 +942,6 @@
     void addChildToDrawing(const sp<Layer>&);
     void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
 
-    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
-    // the settings clears the content with a solid black fill.
-    void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
-    void prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect);
-
     void prepareBasicGeometryCompositionState();
     void prepareGeometryCompositionState();
     void prepareCursorCompositionState();
@@ -991,7 +974,7 @@
      * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
      * in this layer's space, regardless of the specified crop layer.
      */
-    virtual Rect getInputBounds() const;
+    std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const;
 
     bool mPremultipliedAlpha{true};
     const std::string mName;
@@ -1000,6 +983,12 @@
     // These are only accessed by the main thread or the tracing thread.
     State mDrawingState;
 
+    TrustedPresentationThresholds mTrustedPresentationThresholds;
+    TrustedPresentationListener mTrustedPresentationListener;
+    bool mLastComputedTrustedPresentationState = false;
+    bool mLastReportedTrustedPresentationState = false;
+    int64_t mEnteredTrustedPresentationStateTime = -1;
+
     uint32_t mTransactionFlags{0};
     // Updated in doTransaction, used to track the last sequence number we
     // committed. Currently this is really only used for updating visible
@@ -1059,7 +1048,14 @@
     sp<Fence> mLastClientCompositionFence;
     bool mClearClientCompositionFenceOnLayerDisplayed = false;
 private:
-    virtual void setTransformHint(ui::Transform::RotationFlags) {}
+    friend class SlotGenerationTest;
+    friend class TransactionFrameTracerTest;
+    friend class TransactionSurfaceFrameTest;
+
+    bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
+    bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
+
+    std::atomic<bool> mSidebandStreamChanged{false};
 
     // Returns true if the layer can draw shadows on its border.
     virtual bool canDrawShadows() const { return true; }
@@ -1083,7 +1079,6 @@
 
     void updateTreeHasFrameRateVote();
     bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded);
-    bool setFrameRateForLayerTree(FrameRate);
     void setZOrderRelativeOf(const wp<Layer>& relativeOf);
     bool isTrustedOverlay() const;
     gui::DropInputMode getDropInputMode() const;
@@ -1104,6 +1099,47 @@
     // Fills in the frame and transform info for the gui::WindowInfo.
     void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
 
+    inline void tracePendingBufferCount(int32_t pendingBuffers);
+
+    // Latch sideband stream and returns true if the dirty region should be updated.
+    bool latchSidebandStream(bool& recomputeVisibleRegions);
+
+    bool hasFrameUpdate() const;
+
+    void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false);
+
+    // Crop that applies to the buffer
+    Rect computeBufferCrop(const State& s);
+
+    void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
+                                   const sp<GraphicBuffer>& buffer, uint64_t framenumber,
+                                   const sp<Fence>& releaseFence,
+                                   uint32_t currentMaxAcquiredBufferCount);
+
+    // Returns true if the transformed buffer size does not match the layer size and we need
+    // to apply filtering.
+    bool bufferNeedsFiltering() const;
+
+    // Returns true if there is a valid color to fill.
+    bool fillsColor() const;
+    // Returns true if this layer has a blur value.
+    bool hasBlur() const;
+    bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+    bool hasBufferOrSidebandStream() const {
+        return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
+    }
+
+    bool hasBufferOrSidebandStreamInDrawing() const {
+        return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr));
+    }
+
+    bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
+
+    // Fills the provided vector with the currently available JankData and removes the processed
+    // JankData from the pending list.
+    void transferAvailableJankData(const std::deque<sp<CallbackHandle>>& handles,
+                                   std::vector<JankData>& jankData);
+
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
     // a transform from the current layer coordinate space to display(screen) coordinate space.
@@ -1122,11 +1158,6 @@
 
     bool mGetHandleCalled = false;
 
-    // Tracks the process and user id of the caller when creating this layer
-    // to help debugging.
-    pid_t mCallingPid;
-    uid_t mCallingUid;
-
     // The current layer is a clone of mClonedFrom. This means that this layer will update it's
     // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
     // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1139,7 +1170,7 @@
     float mEffectiveShadowRadius = 0.f;
 
     // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
-    GameMode mGameMode = GameMode::Unsupported;
+    gui::GameMode mGameMode = gui::GameMode::Unsupported;
 
     // A list of regions on this layer that should have blurs.
     const std::vector<BlurRegion> getBlurRegions() const;
@@ -1147,7 +1178,61 @@
     bool mIsAtRoot = false;
 
     uint32_t mLayerCreationFlags;
+
     bool findInHierarchy(const sp<Layer>&);
+
+    bool mBorderEnabled = false;
+    float mBorderWidth;
+    half4 mBorderColor;
+
+    void setTransformHintLegacy(ui::Transform::RotationFlags);
+    void resetDrawingStateBufferInfo();
+
+    const uint32_t mTextureName;
+
+    // Transform hint provided to the producer. This must be accessed holding
+    // the mStateLock.
+    ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
+    bool mSkipReportingTransformHint = true;
+    std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
+
+    ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
+    sp<IBinder> mPreviousReleaseBufferEndpoint;
+    uint64_t mPreviousReleasedFrameNumber = 0;
+
+    uint64_t mPreviousBarrierFrameNumber = 0;
+
+    bool mReleasePreviousBuffer = false;
+
+    // Stores the last set acquire fence signal time used to populate the callback handle's acquire
+    // time.
+    std::variant<nsecs_t, sp<Fence>> mCallbackHandleAcquireTimeOrFence = -1;
+
+    std::deque<std::shared_ptr<android::frametimeline::SurfaceFrame>> mPendingJankClassifications;
+    // An upper bound on the number of SurfaceFrames in the pending classifications deque.
+    static constexpr int kPendingClassificationMaxSurfaceFrames = 50;
+
+    const std::string mBlastTransactionName{"BufferTX - " + mName};
+    // This integer is incremented everytime a buffer arrives at the server for this layer,
+    // and decremented when a buffer is dropped or latched. When changed the integer is exported
+    // to systrace with ATRACE_INT and mBlastTransactionName. This way when debugging perf it is
+    // possible to see when a buffer arrived at the server, and in which frame it latched.
+    //
+    // You can understand the trace this way:
+    //     - 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 or doTransaction, a buffer was dropped
+    std::atomic<int32_t> mPendingBufferTransactions{0};
+
+    // Contains requested position and matrix updates. This will be applied if the client does
+    // not specify a destination frame.
+    ui::Transform mRequestedTransform;
+
+    sp<LayerFE> mLegacyLayerFE;
+    std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
+    std::unique_ptr<frontend::LayerSnapshot> mSnapshot =
+            std::make_unique<frontend::LayerSnapshot>();
+    bool mHandleAlive = false;
 };
 
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
new file mode 100644
index 0000000..f855f27
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "LayerFE"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <gui/GLConsumer.h>
+#include <gui/TraceUtils.h>
+#include <math/vec3.h>
+#include <system/window.h>
+#include <utils/Log.h>
+
+#include "LayerFE.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+namespace {
+constexpr float defaultMaxLuminance = 1000.0;
+
+constexpr mat4 inverseOrientation(uint32_t transform) {
+    const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+    const mat4 rot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    mat4 tr;
+
+    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        tr = tr * rot90;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+        tr = tr * flipH;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+        tr = tr * flipV;
+    }
+    return inverse(tr);
+}
+
+FloatRect reduce(const FloatRect& win, const Region& exclude) {
+    if (CC_LIKELY(exclude.isEmpty())) {
+        return win;
+    }
+    // Convert through Rect (by rounding) for lack of FloatRegion
+    return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
+}
+
+// Computes the transform matrix using the setFilteringEnabled to determine whether the
+// transform matrix should be computed for use with bilinear filtering.
+void getDrawingTransformMatrix(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                               Rect bufferCrop, uint32_t bufferTransform, bool filteringEnabled,
+                               float outMatrix[16]) {
+    if (!buffer) {
+        ALOGE("Buffer should not be null!");
+        return;
+    }
+    GLConsumer::computeTransformMatrix(outMatrix, static_cast<float>(buffer->getWidth()),
+                                       static_cast<float>(buffer->getHeight()),
+                                       buffer->getPixelFormat(), bufferCrop, bufferTransform,
+                                       filteringEnabled);
+}
+
+} // namespace
+
+LayerFE::LayerFE(const std::string& name) : mName(name) {}
+
+const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const {
+    return mSnapshot.get();
+}
+
+bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) {
+    mCompositionResult.refreshStartTime = refreshStartTime;
+    return mSnapshot->hasReadyFrame;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientComposition(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientCompositionInternal(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
+        return {};
+    }
+
+    // HWC requests to clear this layer.
+    if (targetSettings.clearContent) {
+        prepareClearClientComposition(*layerSettings, false /* blackout */);
+        return layerSettings;
+    }
+
+    // set the shadow for the layer if needed
+    prepareShadowClientComposition(*layerSettings, targetSettings.viewport);
+
+    return layerSettings;
+}
+
+std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientCompositionInternal(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    ATRACE_CALL();
+    compositionengine::LayerFE::LayerSettings layerSettings;
+    layerSettings.geometry.boundaries =
+            reduce(mSnapshot->geomLayerBounds, mSnapshot->transparentRegionHint);
+    layerSettings.geometry.positionTransform = mSnapshot->geomLayerTransform.asMatrix4();
+
+    // skip drawing content if the targetSettings indicate the content will be occluded
+    const bool drawContent = targetSettings.realContentIsVisible || targetSettings.clearContent;
+    layerSettings.skipContentDraw = !drawContent;
+
+    if (!mSnapshot->colorTransformIsIdentity) {
+        layerSettings.colorTransform = mSnapshot->colorTransform;
+    }
+
+    const auto& roundedCornerState = mSnapshot->roundedCorner;
+    layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
+    layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+
+    layerSettings.alpha = mSnapshot->alpha;
+    layerSettings.sourceDataspace = mSnapshot->dataspace;
+
+    // Override the dataspace transfer from 170M to sRGB if the device configuration requests this.
+    // We do this here instead of in buffer info so that dumpsys can still report layers that are
+    // using the 170M transfer.
+    if (targetSettings.treat170mAsSrgb &&
+        (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
+                HAL_DATASPACE_TRANSFER_SMPTE_170M) {
+        layerSettings.sourceDataspace = static_cast<ui::Dataspace>(
+                (layerSettings.sourceDataspace & HAL_DATASPACE_STANDARD_MASK) |
+                (layerSettings.sourceDataspace & HAL_DATASPACE_RANGE_MASK) |
+                HAL_DATASPACE_TRANSFER_SRGB);
+    }
+
+    layerSettings.whitePointNits = targetSettings.whitePointNits;
+    switch (targetSettings.blurSetting) {
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
+            layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+            layerSettings.blurRegions = mSnapshot->blurRegions;
+            layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4();
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
+            layerSettings.backgroundBlurRadius = mSnapshot->backgroundBlurRadius;
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
+            layerSettings.blurRegions = mSnapshot->blurRegions;
+            layerSettings.blurRegionTransform = mSnapshot->localTransformInverse.asMatrix4();
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
+        default:
+            break;
+    }
+    layerSettings.stretchEffect = mSnapshot->stretchEffect;
+    // Record the name of the layer for debugging further down the stack.
+    layerSettings.name = mSnapshot->name;
+
+    if (hasEffect() && !hasBufferOrSidebandStream()) {
+        prepareEffectsClientComposition(layerSettings, targetSettings);
+        return layerSettings;
+    }
+
+    prepareBufferStateClientComposition(layerSettings, targetSettings);
+    return layerSettings;
+}
+
+void LayerFE::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
+                                            bool blackout) const {
+    layerSettings.source.buffer.buffer = nullptr;
+    layerSettings.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+    layerSettings.disableBlending = true;
+    layerSettings.bufferId = 0;
+    layerSettings.frameNumber = 0;
+
+    // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
+    layerSettings.alpha = blackout ? 1.0f : 0.0f;
+    layerSettings.name = mSnapshot->name;
+}
+
+void LayerFE::prepareEffectsClientComposition(
+        compositionengine::LayerFE::LayerSettings& layerSettings,
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+    if (targetSettings.realContentIsVisible && fillsColor()) {
+        // Set color for color fill settings.
+        layerSettings.source.solidColor = mSnapshot->color.rgb;
+    } else if (hasBlur() || drawShadows()) {
+        layerSettings.skipContentDraw = true;
+    }
+}
+
+void LayerFE::prepareBufferStateClientComposition(
+        compositionengine::LayerFE::LayerSettings& layerSettings,
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) const {
+    ATRACE_CALL();
+    if (CC_UNLIKELY(!mSnapshot->externalTexture)) {
+        // If there is no buffer for the layer or we have sidebandstream where there is no
+        // activeBuffer, then we need to return LayerSettings.
+        return;
+    }
+    const bool blackOutLayer =
+            (mSnapshot->hasProtectedContent && !targetSettings.supportsProtectedContent) ||
+            ((mSnapshot->isSecure || mSnapshot->hasProtectedContent) && !targetSettings.isSecure);
+    const bool bufferCanBeUsedAsHwTexture =
+            mSnapshot->externalTexture->getUsage() & GraphicBuffer::USAGE_HW_TEXTURE;
+    if (blackOutLayer || !bufferCanBeUsedAsHwTexture) {
+        ALOGE_IF(!bufferCanBeUsedAsHwTexture, "%s is blacked out as buffer is not gpu readable",
+                 mSnapshot->name.c_str());
+        prepareClearClientComposition(layerSettings, true /* blackout */);
+        return;
+    }
+
+    layerSettings.source.buffer.buffer = mSnapshot->externalTexture;
+    layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque;
+    layerSettings.source.buffer.fence = mSnapshot->acquireFence;
+    layerSettings.source.buffer.textureName = mSnapshot->textureName;
+    layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha;
+    layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410;
+    bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+    bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
+    float maxLuminance = 0.f;
+    if (hasSmpte2086 && hasCta861_3) {
+        maxLuminance = std::min(mSnapshot->hdrMetadata.smpte2086.maxLuminance,
+                                mSnapshot->hdrMetadata.cta8613.maxContentLightLevel);
+    } else if (hasSmpte2086) {
+        maxLuminance = mSnapshot->hdrMetadata.smpte2086.maxLuminance;
+    } else if (hasCta861_3) {
+        maxLuminance = mSnapshot->hdrMetadata.cta8613.maxContentLightLevel;
+    } else {
+        switch (layerSettings.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+            case HAL_DATASPACE_TRANSFER_ST2084:
+            case HAL_DATASPACE_TRANSFER_HLG:
+                // Behavior-match previous releases for HDR content
+                maxLuminance = defaultMaxLuminance;
+                break;
+        }
+    }
+    layerSettings.source.buffer.maxLuminanceNits = maxLuminance;
+    layerSettings.frameNumber = mSnapshot->frameNumber;
+    layerSettings.bufferId = mSnapshot->externalTexture->getId();
+
+    // Query the texture matrix given our current filtering mode.
+    float textureMatrix[16];
+    getDrawingTransformMatrix(layerSettings.source.buffer.buffer, mSnapshot->geomContentCrop,
+                              mSnapshot->geomBufferTransform, targetSettings.needsFiltering,
+                              textureMatrix);
+
+    if (mSnapshot->geomBufferUsesDisplayInverseTransform) {
+        /*
+         * the code below applies the primary display's inverse transform to
+         * the texture transform
+         */
+        uint32_t transform = SurfaceFlinger::getActiveDisplayRotationFlags();
+        mat4 tr = inverseOrientation(transform);
+
+        /**
+         * TODO(b/36727915): This is basically a hack.
+         *
+         * Ensure that regardless of the parent transformation,
+         * this buffer is always transformed from native display
+         * orientation to display orientation. For example, in the case
+         * of a camera where the buffer remains in native orientation,
+         * we want the pixels to always be upright.
+         */
+        const auto parentTransform = mSnapshot->parentTransform;
+        tr = tr * inverseOrientation(parentTransform.getOrientation());
+
+        // and finally apply it to the original texture matrix
+        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+    }
+
+    const Rect win{layerSettings.geometry.boundaries};
+    float bufferWidth = static_cast<float>(mSnapshot->bufferSize.getWidth());
+    float bufferHeight = static_cast<float>(mSnapshot->bufferSize.getHeight());
+
+    // Layers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+    // ignore them.
+    if (!mSnapshot->bufferSize.isValid()) {
+        bufferWidth = float(win.right) - float(win.left);
+        bufferHeight = float(win.bottom) - float(win.top);
+    }
+
+    const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+    const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+    const float translateY = float(win.top) / bufferHeight;
+    const float translateX = float(win.left) / bufferWidth;
+
+    // Flip y-coordinates because GLConsumer expects OpenGL convention.
+    mat4 tr = mat4::translate(vec4(.5f, .5f, 0.f, 1.f)) * mat4::scale(vec4(1.f, -1.f, 1.f, 1.f)) *
+            mat4::translate(vec4(-.5f, -.5f, 0.f, 1.f)) *
+            mat4::translate(vec4(translateX, translateY, 0.f, 1.f)) *
+            mat4::scale(vec4(scaleWidth, scaleHeight, 1.0f, 1.0f));
+
+    layerSettings.source.buffer.useTextureFiltering = targetSettings.needsFiltering;
+    layerSettings.source.buffer.textureTransform =
+            mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+    return;
+}
+
+void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+                                             const Rect& layerStackRect) const {
+    renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+    if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
+        return;
+    }
+
+    // Shift the spot light x-position to the middle of the display and then
+    // offset it by casting layer's screen pos.
+    state.lightPos.x =
+            (static_cast<float>(layerStackRect.width()) / 2.f) - mSnapshot->transformedBounds.left;
+    state.lightPos.y -= mSnapshot->transformedBounds.top;
+    caster.shadow = state;
+}
+
+void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+                               ui::LayerStack layerStack) {
+    mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack);
+}
+
+CompositionResult&& LayerFE::stealCompositionResult() {
+    return std::move(mCompositionResult);
+}
+
+const char* LayerFE::getDebugName() const {
+    return mName.c_str();
+}
+
+const LayerMetadata* LayerFE::getMetadata() const {
+    return &mSnapshot->layerMetadata;
+}
+
+const LayerMetadata* LayerFE::getRelativeMetadata() const {
+    return &mSnapshot->relativeLayerMetadata;
+}
+
+int32_t LayerFE::getSequence() const {
+    return mSnapshot->sequence;
+}
+
+bool LayerFE::hasRoundedCorners() const {
+    return mSnapshot->roundedCorner.hasRoundedCorners();
+}
+
+void LayerFE::setWasClientComposed(const sp<Fence>& fence) {
+    mCompositionResult.lastClientCompositionFence = fence;
+}
+
+bool LayerFE::hasBufferOrSidebandStream() const {
+    return mSnapshot->externalTexture || mSnapshot->sidebandStream;
+}
+
+bool LayerFE::fillsColor() const {
+    return mSnapshot->color.r >= 0.0_hf && mSnapshot->color.g >= 0.0_hf &&
+            mSnapshot->color.b >= 0.0_hf;
+}
+
+bool LayerFE::hasBlur() const {
+    return mSnapshot->backgroundBlurRadius > 0 || mSnapshot->blurRegions.size() > 0;
+}
+
+bool LayerFE::drawShadows() const {
+    return mSnapshot->shadowSettings.length > 0.f &&
+            (mSnapshot->shadowSettings.ambientColor.a > 0 ||
+             mSnapshot->shadowSettings.spotColor.a > 0);
+};
+
+const sp<GraphicBuffer> LayerFE::getBuffer() const {
+    return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
new file mode 100644
index 0000000..d584fb7
--- /dev/null
+++ b/services/surfaceflinger/LayerFE.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <android/gui/CachingHint.h>
+#include <gui/LayerMetadata.h>
+#include "FrontEnd/LayerSnapshot.h"
+#include "compositionengine/LayerFE.h"
+#include "compositionengine/LayerFECompositionState.h"
+#include "renderengine/LayerSettings.h"
+
+namespace android {
+
+struct CompositionResult {
+    // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
+    // and remove this field.
+    nsecs_t refreshStartTime = 0;
+    std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
+    sp<Fence> lastClientCompositionFence = nullptr;
+};
+
+class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
+public:
+    LayerFE(const std::string& name);
+
+    // compositionengine::LayerFE overrides
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t refreshStartTime, 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;
+    void setWasClientComposed(const sp<Fence>&) override;
+    const gui::LayerMetadata* getMetadata() const override;
+    const gui::LayerMetadata* getRelativeMetadata() const override;
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+    CompositionResult&& stealCompositionResult();
+
+    std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
+
+private:
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientCompositionInternal(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+    // the settings clears the content with a solid black fill.
+    void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const;
+    void prepareShadowClientComposition(LayerFE::LayerSettings& caster,
+                                        const Rect& layerStackRect) const;
+    void prepareBufferStateClientComposition(
+            compositionengine::LayerFE::LayerSettings&,
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+    void prepareEffectsClientComposition(
+            compositionengine::LayerFE::LayerSettings&,
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
+
+    bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
+    bool hasBufferOrSidebandStream() const;
+
+    bool fillsColor() const;
+    bool hasBlur() const;
+    bool drawShadows() const;
+
+    const sp<GraphicBuffer> getBuffer() const;
+
+    CompositionResult mCompositionResult;
+    std::string mName;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 0506c47..3472d20 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -15,6 +15,8 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerSnapshot.h"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wextra"
@@ -247,6 +249,218 @@
     outRegion.right = proto.right();
     outRegion.bottom = proto.bottom();
 }
+
+LayersProto LayerProtoFromSnapshotGenerator::generate(const frontend::LayerHierarchy& root) {
+    mLayersProto.clear_layers();
+    std::unordered_set<uint64_t> stackIdsToSkip;
+    if ((mTraceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+        for (const auto& [layerStack, displayInfo] : mDisplayInfos) {
+            if (displayInfo.isVirtual) {
+                stackIdsToSkip.insert(layerStack.id);
+            }
+        }
+    }
+
+    frontend::LayerHierarchy::TraversalPath path = frontend::LayerHierarchy::TraversalPath::ROOT;
+    for (auto& [child, variant] : root.mChildren) {
+        if (variant != frontend::LayerHierarchy::Variant::Attached ||
+            stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) {
+            continue;
+        }
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, path);
+    }
+
+    // fill in relative and parent info
+    for (int i = 0; i < mLayersProto.layers_size(); i++) {
+        auto layerProto = mLayersProto.mutable_layers()->Mutable(i);
+        auto it = mChildToRelativeParent.find(layerProto->id());
+        if (it == mChildToRelativeParent.end()) {
+            layerProto->set_z_order_relative_of(-1);
+        } else {
+            layerProto->set_z_order_relative_of(it->second);
+        }
+        it = mChildToParent.find(layerProto->id());
+        if (it == mChildToParent.end()) {
+            layerProto->set_parent(-1);
+        } else {
+            layerProto->set_parent(it->second);
+        }
+    }
+
+    mDefaultSnapshots.clear();
+    mChildToRelativeParent.clear();
+    return std::move(mLayersProto);
+}
+
+frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot(
+        frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer) {
+    frontend::LayerSnapshot* snapshot = mSnapshotBuilder.getSnapshot(path);
+    if (snapshot) {
+        return snapshot;
+    } else {
+        mDefaultSnapshots[path] = frontend::LayerSnapshot(layer, path);
+        return &mDefaultSnapshots[path];
+    }
+}
+
+void LayerProtoFromSnapshotGenerator::writeHierarchyToProto(
+        const frontend::LayerHierarchy& root, frontend::LayerHierarchy::TraversalPath& path) {
+    using Variant = frontend::LayerHierarchy::Variant;
+    LayerProto* layerProto = mLayersProto.add_layers();
+    const frontend::RequestedLayerState& layer = *root.getLayer();
+    frontend::LayerSnapshot* snapshot = getSnapshot(path, layer);
+    LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags);
+
+    for (const auto& [child, variant] : root.mChildren) {
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer);
+        if (variant == Variant::Attached || variant == Variant::Detached ||
+            variant == Variant::Mirror) {
+            mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
+            layerProto->add_children(childSnapshot->uniqueSequence);
+        } else if (variant == Variant::Relative) {
+            mChildToRelativeParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
+            layerProto->add_relatives(childSnapshot->uniqueSequence);
+        }
+    }
+
+    if (mTraceFlags & LayerTracing::TRACE_COMPOSITION) {
+        auto it = mLegacyLayers.find(layer.id);
+        if (it != mLegacyLayers.end()) {
+            it->second->writeCompositionStateToProto(layerProto, snapshot->outputFilter.layerStack);
+        }
+    }
+
+    for (const auto& [child, variant] : root.mChildren) {
+        // avoid visiting relative layers twice
+        if (variant == Variant::Detached) {
+            continue;
+        }
+        frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path,
+                                                                          child->getLayer()->id,
+                                                                          variant);
+        writeHierarchyToProto(*child, path);
+    }
+}
+
+void LayerProtoHelper::writeSnapshotToProto(LayerProto* layerInfo,
+                                            const frontend::RequestedLayerState& requestedState,
+                                            const frontend::LayerSnapshot& snapshot,
+                                            uint32_t traceFlags) {
+    const ui::Transform transform = snapshot.geomLayerTransform;
+    auto buffer = requestedState.externalTexture;
+    if (buffer != nullptr) {
+        LayerProtoHelper::writeToProto(*buffer,
+                                       [&]() { return layerInfo->mutable_active_buffer(); });
+        LayerProtoHelper::writeToProtoDeprecated(ui::Transform(requestedState.bufferTransform),
+                                                 layerInfo->mutable_buffer_transform());
+    }
+    layerInfo->set_invalidate(snapshot.contentDirty);
+    layerInfo->set_is_protected(snapshot.hasProtectedContent);
+    layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(snapshot.dataspace)));
+    layerInfo->set_curr_frame(requestedState.bufferData->frameNumber);
+    layerInfo->set_requested_corner_radius(requestedState.cornerRadius);
+    layerInfo->set_corner_radius(
+            (snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0);
+    layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius);
+    layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay);
+    LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+    LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+                                           [&]() { return layerInfo->mutable_position(); });
+    LayerProtoHelper::writeToProto(snapshot.geomLayerBounds,
+                                   [&]() { return layerInfo->mutable_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.surfaceDamage,
+                                   [&]() { return layerInfo->mutable_damage_region(); });
+
+    if (requestedState.hasColorTransform) {
+        LayerProtoHelper::writeToProto(snapshot.colorTransform,
+                                       layerInfo->mutable_color_transform());
+    }
+
+    LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+                                   [&]() { return layerInfo->mutable_source_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.transformedBounds,
+                                   [&]() { return layerInfo->mutable_screen_bounds(); });
+    LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
+                                   [&]() { return layerInfo->mutable_corner_radius_crop(); });
+    layerInfo->set_shadow_radius(snapshot.shadowRadius);
+
+    layerInfo->set_id(snapshot.uniqueSequence);
+    layerInfo->set_original_id(snapshot.sequence);
+    if (!snapshot.path.isClone()) {
+        layerInfo->set_name(requestedState.name);
+    } else {
+        layerInfo->set_name(requestedState.name + "(Mirror)");
+    }
+    layerInfo->set_type("Layer");
+
+    LayerProtoHelper::writeToProto(requestedState.transparentRegion,
+                                   [&]() { return layerInfo->mutable_transparent_region(); });
+
+    layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id);
+    layerInfo->set_z(requestedState.z);
+
+    ui::Transform requestedTransform = requestedState.getTransform(0);
+    LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+        return layerInfo->mutable_requested_position();
+    });
+
+    LayerProtoHelper::writeToProto(requestedState.crop,
+                                   [&]() { return layerInfo->mutable_crop(); });
+
+    layerInfo->set_is_opaque(snapshot.contentOpaque);
+    if (requestedState.externalTexture)
+        layerInfo->set_pixel_format(
+                decodePixelFormat(requestedState.externalTexture->getPixelFormat()));
+    LayerProtoHelper::writeToProto(snapshot.color, [&]() { return layerInfo->mutable_color(); });
+    LayerProtoHelper::writeToProto(requestedState.color,
+                                   [&]() { return layerInfo->mutable_requested_color(); });
+    layerInfo->set_flags(requestedState.flags);
+
+    LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+                                             layerInfo->mutable_requested_transform());
+
+    layerInfo->set_is_relative_of(requestedState.isRelativeOf);
+
+    layerInfo->set_owner_uid(requestedState.ownerUid);
+
+    if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
+        LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+                                       [&]() { return layerInfo->mutable_input_window_info(); });
+    }
+
+    if (traceFlags & LayerTracing::TRACE_EXTRA) {
+        auto protoMap = layerInfo->mutable_metadata();
+        for (const auto& entry : requestedState.metadata.mMap) {
+            (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
+        }
+    }
+
+    LayerProtoHelper::writeToProto(requestedState.destinationFrame,
+                                   [&]() { return layerInfo->mutable_destination_frame(); });
+}
+
+google::protobuf::RepeatedPtrField<DisplayProto> LayerProtoHelper::writeDisplayInfoToProto(
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos) {
+    google::protobuf::RepeatedPtrField<DisplayProto> displays;
+    displays.Reserve(displayInfos.size());
+    for (const auto& [layerStack, displayInfo] : displayInfos) {
+        auto displayProto = displays.Add();
+        displayProto->set_id(displayInfo.info.displayId);
+        displayProto->set_layer_stack(layerStack.id);
+        displayProto->mutable_size()->set_w(displayInfo.info.logicalWidth);
+        displayProto->mutable_size()->set_h(displayInfo.info.logicalHeight);
+        writeTransformToProto(displayInfo.transform, displayProto->mutable_transform());
+        displayProto->set_is_virtual(displayInfo.isVirtual);
+    }
+    return displays;
+}
+
 } // namespace surfaceflinger
 } // namespace android
 
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 6ade143..b84a49b 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -25,6 +25,9 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
+#include <cstdint>
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerSnapshot.h"
 
 namespace android {
 namespace surfaceflinger {
@@ -58,6 +61,44 @@
     static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
     static void writeToProto(const android::BlurRegion region, BlurRegion*);
     static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
+    static void writeSnapshotToProto(LayerProto* outProto,
+                                     const frontend::RequestedLayerState& requestedState,
+                                     const frontend::LayerSnapshot& snapshot, uint32_t traceFlags);
+    static google::protobuf::RepeatedPtrField<DisplayProto> writeDisplayInfoToProto(
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos);
+};
+
+class LayerProtoFromSnapshotGenerator {
+public:
+    LayerProtoFromSnapshotGenerator(
+            const frontend::LayerSnapshotBuilder& snapshotBuilder,
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+            const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags)
+          : mSnapshotBuilder(snapshotBuilder),
+            mLegacyLayers(legacyLayers),
+            mDisplayInfos(displayInfos),
+            mTraceFlags(traceFlags) {}
+    LayersProto generate(const frontend::LayerHierarchy& root);
+
+private:
+    void writeHierarchyToProto(const frontend::LayerHierarchy& root,
+                               frontend::LayerHierarchy::TraversalPath& path);
+    frontend::LayerSnapshot* getSnapshot(frontend::LayerHierarchy::TraversalPath& path,
+                                         const frontend::RequestedLayerState& layer);
+
+    const frontend::LayerSnapshotBuilder& mSnapshotBuilder;
+    const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers;
+    const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& mDisplayInfos;
+    uint32_t mTraceFlags;
+    LayersProto mLayersProto;
+    // winscope expects all the layers, so provide a snapshot even if it not currently drawing
+    std::unordered_map<frontend::LayerHierarchy::TraversalPath, frontend::LayerSnapshot,
+                       frontend::LayerHierarchy::TraversalPathHash>
+            mDefaultSnapshots;
+    std::unordered_map<uint32_t /* child unique seq*/, uint32_t /* relative parent unique seq*/>
+            mChildToRelativeParent;
+    std::unordered_map<uint32_t /* child unique seq*/, uint32_t /* parent unique seq*/>
+            mChildToParent;
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
deleted file mode 100644
index 1c0263b..0000000
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2011 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 "LayerRejecter.h"
-
-#include <gui/BufferItem.h>
-#include <system/window.h>
-
-#define DEBUG_RESIZE 0
-
-namespace android {
-
-LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
-                             bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
-                             bool transformToDisplayInverse)
-      : mFront(front),
-        mCurrent(current),
-        mRecomputeVisibleRegions(recomputeVisibleRegions),
-        mStickyTransformSet(stickySet),
-        mName(name),
-        mTransformToDisplayInverse(transformToDisplayInverse) {}
-
-bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
-    if (buf == nullptr) {
-        return false;
-    }
-
-    uint32_t bufWidth = buf->getWidth();
-    uint32_t bufHeight = buf->getHeight();
-
-    // check that we received a buffer of the right size
-    // (Take the buffer's orientation into account)
-    if (item.mTransform & ui::Transform::ROT_90) {
-        std::swap(bufWidth, bufHeight);
-    }
-
-    if (mTransformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
-        if (invTransform & ui::Transform::ROT_90) {
-            std::swap(bufWidth, bufHeight);
-        }
-    }
-
-    int actualScalingMode = item.mScalingMode;
-    bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
-    if (mFront.active_legacy != mFront.requested_legacy) {
-        if (isFixedSize ||
-            (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) {
-            // Here we pretend the transaction happened by updating the
-            // current and drawing states. Drawing state is only accessed
-            // in this thread, no need to have it locked
-            mFront.active_legacy = mFront.requested_legacy;
-
-            // We also need to update the current state so that
-            // we don't end-up overwriting the drawing state with
-            // this stale current state during the next transaction
-            //
-            // NOTE: We don't need to hold the transaction lock here
-            // because State::active_legacy is only accessed from this thread.
-            mCurrent.active_legacy = mFront.active_legacy;
-            mCurrent.modified = true;
-
-            // recompute visible region
-            mRecomputeVisibleRegions = true;
-
-            if (mFront.crop != mFront.requestedCrop) {
-                mFront.crop = mFront.requestedCrop;
-                mCurrent.crop = mFront.requestedCrop;
-                mRecomputeVisibleRegions = true;
-            }
-        }
-
-        ALOGD_IF(DEBUG_RESIZE,
-                 "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n"
-                 "  drawing={ active_legacy   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} "
-                 "(%4d,%4d) "
-                 "}\n"
-                 "            requested_legacy={ wh={%4u,%4u} }}\n",
-                 mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
-                 mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop.left, mFront.crop.top,
-                 mFront.crop.right, mFront.crop.bottom, mFront.crop.getWidth(),
-                 mFront.crop.getHeight(), mFront.requested_legacy.w, mFront.requested_legacy.h);
-    }
-
-    if (!isFixedSize && !mStickyTransformSet) {
-        if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) {
-            // reject this buffer
-            ALOGE("[%s] rejecting buffer: "
-                  "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
-                  mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
-                  mFront.active_legacy.h);
-            return true;
-        }
-    }
-
-    // if the transparent region has changed (this test is
-    // conservative, but that's fine, worst case we're doing
-    // a bit of extra work), we latch the new one and we
-    // trigger a visible-region recompute.
-    //
-    // We latch the transparent region here, instead of above where we latch
-    // the rest of the geometry because it is only content but not necessarily
-    // resize dependent.
-    if (!mFront.activeTransparentRegion_legacy.hasSameRects(
-                mFront.requestedTransparentRegion_legacy)) {
-        mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
-
-        // We also need to update the current state so that
-        // we don't end-up overwriting the drawing state with
-        // this stale current state during the next transaction
-        //
-        // NOTE: We don't need to hold the transaction lock here
-        // because State::active_legacy is only accessed from this thread.
-        mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy;
-
-        // recompute visible region
-        mRecomputeVisibleRegions = true;
-    }
-
-    return false;
-}
-
-}  // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
deleted file mode 100644
index 4981f45..0000000
--- a/services/surfaceflinger/LayerRejecter.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 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 "Layer.h"
-#include "BufferLayerConsumer.h"
-
-namespace android {
-
-class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-public:
-    LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
-                  bool stickySet, const std::string& name,
-                  bool transformToDisplayInverse);
-
-    virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
-
-private:
-    Layer::State& mFront;
-    Layer::State& mCurrent;
-    bool& mRecomputeVisibleRegions;
-    const bool mStickyTransformSet;
-    const std::string& mName;
-    const bool mTransformToDisplayInverse;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 896f254..51d4ff8 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -17,8 +17,8 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Transform.h>
 
-#include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "FrontEnd/LayerCreationArgs.h"
 #include "Layer.h"
 #include "LayerRenderArea.h"
 #include "SurfaceFlinger.h"
@@ -31,6 +31,7 @@
         // Compute and cache the bounds for the new parent layer.
         newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
             0.f /* shadowRadius */);
+        newParent->updateSnapshot(true /* updateGeometry */);
         oldParent->setChildrenDrawingParent(newParent);
 };
 
@@ -38,9 +39,13 @@
 
 LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
                                  ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
-                                 const Rect& layerStackRect, bool allowSecureLayers)
-      : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers),
+                                 bool allowSecureLayers, const ui::Transform& layerTransform,
+                                 const Rect& layerBufferSize, bool hintForSeamlessTransition)
+      : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, hintForSeamlessTransition,
+                   allowSecureLayers),
         mLayer(std::move(layer)),
+        mLayerTransform(layerTransform),
+        mLayerBufferSize(layerBufferSize),
         mCrop(crop),
         mFlinger(flinger),
         mChildrenOnly(childrenOnly) {}
@@ -49,33 +54,18 @@
     return mTransform;
 }
 
-Rect LayerRenderArea::getBounds() const {
-    return mLayer->getBufferSize(mLayer->getDrawingState());
-}
-
-int LayerRenderArea::getHeight() const {
-    return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
-}
-
-int LayerRenderArea::getWidth() const {
-    return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
-}
-
 bool LayerRenderArea::isSecure() const {
     return mAllowSecureLayers;
 }
 
-bool LayerRenderArea::needsFiltering() const {
-    return mNeedsFiltering;
-}
-
 sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
     return nullptr;
 }
 
 Rect LayerRenderArea::getSourceCrop() const {
     if (mCrop.isEmpty()) {
-        return getBounds();
+        // TODO this should probably be mBounds instead of just buffer bounds
+        return mLayerBufferSize;
     } else {
         return mCrop;
     }
@@ -84,20 +74,23 @@
 void LayerRenderArea::render(std::function<void()> drawLayers) {
     using namespace std::string_literals;
 
-    const Rect sourceCrop = getSourceCrop();
-    // no need to check rotation because there is none
-    mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+    if (!mChildrenOnly) {
+        mTransform = mLayerTransform.inverse();
+    }
 
+    if (mFlinger.mLayerLifecycleManagerEnabled) {
+        drawLayers();
+        return;
+    }
     // If layer is offscreen, update mirroring info if it exists
     if (mLayer->isRemovedFromCurrentState()) {
         mLayer->traverse(LayerVector::StateSet::Drawing,
-                         [&](Layer* layer) { layer->updateMirrorInfo(); });
+                         [&](Layer* layer) { layer->updateMirrorInfo({}); });
         mLayer->traverse(LayerVector::StateSet::Drawing,
                          [&](Layer* layer) { layer->updateCloneBufferInfo(); });
     }
 
     if (!mChildrenOnly) {
-        mTransform = mLayer->getTransform().inverse();
         // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
         // layers in a regular cycles.
         if (mLayer->isRemovedFromCurrentState()) {
@@ -110,11 +103,12 @@
         // layer which has no properties set and which does not draw.
         //  We hold the statelock as the reparent-for-drawing operation modifies the
         //  hierarchy and there could be readers on Binder threads, like dump.
-        sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
-                  {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
+        auto screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
+                {&mFlinger, nullptr, "Screenshot Parent"s, ISurfaceComposerClient::eNoColorFill,
+                 LayerMetadata()});
         {
             Mutex::Autolock _l(mFlinger.mStateLock);
-            reparentForDrawing(mLayer, screenshotParentLayer, sourceCrop);
+            reparentForDrawing(mLayer, screenshotParentLayer, getSourceCrop());
         }
         drawLayers();
         {
@@ -122,6 +116,8 @@
             mLayer->setChildrenDrawingParent(mLayer);
         }
     }
+    mLayer->updateSnapshot(/*updateGeometry=*/true);
+    mLayer->updateChildrenSnapshots(/*updateGeometry=*/true);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index 41273e0..aa609ee 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -33,15 +33,12 @@
 class LayerRenderArea : public RenderArea {
 public:
     LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
-                    ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect,
-                    bool allowSecureLayers);
+                    ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
+                    const ui::Transform& layerTransform, const Rect& layerBufferSize,
+                    bool hintForSeamlessTransition);
 
     const ui::Transform& getTransform() const override;
-    Rect getBounds() const override;
-    int getHeight() const override;
-    int getWidth() const override;
     bool isSecure() const override;
-    bool needsFiltering() const override;
     sp<const DisplayDevice> getDisplayDevice() const override;
     Rect getSourceCrop() const override;
 
@@ -50,10 +47,11 @@
 
 private:
     const sp<Layer> mLayer;
+    const ui::Transform mLayerTransform;
+    const Rect mLayerBufferSize;
     const Rect mCrop;
 
     ui::Transform mTransform;
-    bool mNeedsFiltering = false;
 
     SurfaceFlinger& mFlinger;
     const bool mChildrenOnly;
diff --git a/services/surfaceflinger/LocklessQueue.h b/services/surfaceflinger/LocklessQueue.h
new file mode 100644
index 0000000..6b63360
--- /dev/null
+++ b/services/surfaceflinger/LocklessQueue.h
@@ -0,0 +1,82 @@
+/*
+ * 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 <atomic>
+#include <optional>
+
+template <typename T>
+// Single consumer multi producer stack. We can understand the two operations independently to see
+// why they are without race condition.
+//
+// push is responsible for maintaining a linked list stored in mPush, and called from multiple
+// threads without lock. We can see that if two threads never observe the same value from
+// mPush.load, it just functions as a normal linked list. In the case where two threads observe the
+// same value, one of them has to execute the compare_exchange first. The one that doesn't execute
+// the compare exchange first, will receive false from compare_exchange. previousHead is updated (by
+// compare_exchange) to the most recent value of mPush, and we try again. It's relatively clear to
+// see that the process can repeat with an arbitrary number of threads.
+//
+// Pop is much simpler. If mPop is empty (as it begins) it atomically exchanges
+// the entire push list with null. This is safe, since the only other reader (push)
+// of mPush will retry if it changes in between it's read and atomic compare. We
+// then store the list and pop one element.
+//
+// If we already had something in the pop list we just pop directly.
+class LocklessQueue {
+public:
+    class Entry {
+    public:
+        T mValue;
+        std::atomic<Entry*> mNext;
+        Entry(T value) : mValue(value) {}
+    };
+    std::atomic<Entry*> mPush = nullptr;
+    std::atomic<Entry*> mPop = nullptr;
+    bool isEmpty() { return (mPush.load() == nullptr) && (mPop.load() == nullptr); }
+
+    void push(T value) {
+        Entry* entry = new Entry(value);
+        Entry* previousHead = mPush.load(/*std::memory_order_relaxed*/);
+        do {
+            entry->mNext = previousHead;
+        } while (!mPush.compare_exchange_weak(previousHead, entry)); /*std::memory_order_release*/
+    }
+    std::optional<T> pop() {
+        Entry* popped = mPop.load(/*std::memory_order_acquire*/);
+        if (popped) {
+            // Single consumer so this is fine
+            mPop.store(popped->mNext /* , std::memory_order_release */);
+            auto value = popped->mValue;
+            delete popped;
+            return std::move(value);
+        } else {
+            Entry* grabbedList = mPush.exchange(nullptr /* , std::memory_order_acquire */);
+            if (!grabbedList) return std::nullopt;
+            // Reverse the list
+            while (grabbedList->mNext) {
+                Entry* next = grabbedList->mNext;
+                grabbedList->mNext = popped;
+                popped = grabbedList;
+                grabbedList = next;
+            }
+            mPop.store(popped /* , std::memory_order_release */);
+            auto value = grabbedList->mValue;
+            delete grabbedList;
+            return std::move(value);
+        }
+    }
+};
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
deleted file mode 100644
index df76f50..0000000
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright 2014 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 "MonitoredProducer.h"
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-
-#include "Scheduler/MessageQueue.h"
-
-namespace android {
-
-MonitoredProducer::MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-        const sp<SurfaceFlinger>& flinger,
-        const wp<Layer>& layer) :
-    mProducer(producer),
-    mFlinger(flinger),
-    mLayer(layer) {}
-
-MonitoredProducer::~MonitoredProducer() {}
-
-status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
-    return mProducer->requestBuffer(slot, buf);
-}
-
-status_t MonitoredProducer::setMaxDequeuedBufferCount(
-        int maxDequeuedBuffers) {
-    return mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers);
-}
-
-status_t MonitoredProducer::setAsyncMode(bool async) {
-    return mProducer->setAsyncMode(async);
-}
-
-status_t MonitoredProducer::dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                          PixelFormat format, uint64_t usage,
-                                          uint64_t* outBufferAge,
-                                          FrameEventHistoryDelta* outTimestamps) {
-    return mProducer->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps);
-}
-
-status_t MonitoredProducer::detachBuffer(int slot) {
-    return mProducer->detachBuffer(slot);
-}
-
-status_t MonitoredProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-        sp<Fence>* outFence) {
-    return mProducer->detachNextBuffer(outBuffer, outFence);
-}
-
-status_t MonitoredProducer::attachBuffer(int* outSlot,
-        const sp<GraphicBuffer>& buffer) {
-    return mProducer->attachBuffer(outSlot, buffer);
-}
-
-status_t MonitoredProducer::queueBuffer(int slot, const QueueBufferInput& input,
-        QueueBufferOutput* output) {
-    return mProducer->queueBuffer(slot, input, output);
-}
-
-status_t MonitoredProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
-    return mProducer->cancelBuffer(slot, fence);
-}
-
-int MonitoredProducer::query(int what, int* value) {
-    return mProducer->query(what, value);
-}
-
-status_t MonitoredProducer::connect(const sp<IProducerListener>& listener,
-        int api, bool producerControlledByApp, QueueBufferOutput* output) {
-    return mProducer->connect(listener, api, producerControlledByApp, output);
-}
-
-status_t MonitoredProducer::disconnect(int api, DisconnectMode mode) {
-    return mProducer->disconnect(api, mode);
-}
-
-status_t MonitoredProducer::setSidebandStream(const sp<NativeHandle>& stream) {
-    return mProducer->setSidebandStream(stream);
-}
-
-void MonitoredProducer::allocateBuffers(uint32_t width, uint32_t height,
-        PixelFormat format, uint64_t usage) {
-    mProducer->allocateBuffers(width, height, format, usage);
-}
-
-status_t MonitoredProducer::allowAllocation(bool allow) {
-    return mProducer->allowAllocation(allow);
-}
-
-status_t MonitoredProducer::setGenerationNumber(uint32_t generationNumber) {
-    return mProducer->setGenerationNumber(generationNumber);
-}
-
-String8 MonitoredProducer::getConsumerName() const {
-    return mProducer->getConsumerName();
-}
-
-status_t MonitoredProducer::setSharedBufferMode(bool sharedBufferMode) {
-    return mProducer->setSharedBufferMode(sharedBufferMode);
-}
-
-status_t MonitoredProducer::setAutoRefresh(bool autoRefresh) {
-    return mProducer->setAutoRefresh(autoRefresh);
-}
-
-status_t MonitoredProducer::setDequeueTimeout(nsecs_t timeout) {
-    return mProducer->setDequeueTimeout(timeout);
-}
-
-status_t MonitoredProducer::setLegacyBufferDrop(bool drop) {
-    return mProducer->setLegacyBufferDrop(drop);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
-        sp<Fence>* outFence, float outTransformMatrix[16]) {
-    return mProducer->getLastQueuedBuffer(outBuffer, outFence,
-            outTransformMatrix);
-}
-
-status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
-                                                Rect* outRect, uint32_t* outTransform) {
-    return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
-}
-
-void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
-    mProducer->getFrameTimestamps(outDelta);
-}
-
-status_t MonitoredProducer::getUniqueId(uint64_t* outId) const {
-    return mProducer->getUniqueId(outId);
-}
-
-status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const {
-    return mProducer->getConsumerUsage(outUsage);
-}
-
-status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
-    return mProducer->setAutoPrerotation(autoPrerotation);
-}
-
-IBinder* MonitoredProducer::onAsBinder() {
-    return this;
-}
-
-sp<Layer> MonitoredProducer::getLayer() const {
-    return mLayer.promote();
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
deleted file mode 100644
index 3778277..0000000
--- a/services/surfaceflinger/MonitoredProducer.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2014 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.
- */
-
-#ifndef ANDROID_MONITORED_PRODUCER_H
-#define ANDROID_MONITORED_PRODUCER_H
-
-#include <gui/IGraphicBufferProducer.h>
-
-namespace android {
-
-class IProducerListener;
-class NativeHandle;
-class SurfaceFlinger;
-class Layer;
-
-// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
-// be notified upon its destruction
-class MonitoredProducer : public BnGraphicBufferProducer {
-public:
-    MonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-            const sp<SurfaceFlinger>& flinger,
-            const wp<Layer>& layer);
-    virtual ~MonitoredProducer();
-
-    // From IGraphicBufferProducer
-    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
-    virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
-    virtual status_t setAsyncMode(bool async);
-    virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
-                                   PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
-                                   FrameEventHistoryDelta* outTimestamps);
-    virtual status_t detachBuffer(int slot);
-    virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence);
-    virtual status_t attachBuffer(int* outSlot,
-            const sp<GraphicBuffer>& buffer);
-    virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
-            QueueBufferOutput* output);
-    virtual status_t cancelBuffer(int slot, const sp<Fence>& fence);
-    virtual int query(int what, int* value);
-    virtual status_t connect(const sp<IProducerListener>& token, int api,
-            bool producerControlledByApp, QueueBufferOutput* output);
-    virtual status_t disconnect(int api, DisconnectMode mode);
-    virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
-    virtual void allocateBuffers(uint32_t width, uint32_t height,
-            PixelFormat format, uint64_t usage);
-    virtual status_t allowAllocation(bool allow);
-    virtual status_t setGenerationNumber(uint32_t generationNumber);
-    virtual String8 getConsumerName() const override;
-    virtual status_t setDequeueTimeout(nsecs_t timeout) override;
-    virtual status_t setLegacyBufferDrop(bool drop) override;
-    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
-            sp<Fence>* outFence, float outTransformMatrix[16]) override;
-    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
-                                         Rect* outRect, uint32_t* outTransform) override;
-    virtual IBinder* onAsBinder();
-    virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
-    virtual status_t setAutoRefresh(bool autoRefresh) override;
-    virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
-    virtual status_t getUniqueId(uint64_t* outId) const override;
-    virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
-    virtual status_t setAutoPrerotation(bool autoPrerotation) override;
-
-    // The Layer which created this producer, and on which queued Buffer's will be displayed.
-    sp<Layer> getLayer() const;
-
-private:
-    sp<IGraphicBufferProducer> mProducer;
-    sp<SurfaceFlinger> mFlinger;
-    // The Layer which created this producer, and on which queued Buffer's will be displayed.
-    wp<Layer> mLayer;
-};
-
-}; // namespace android
-
-#endif // ANDROID_MONITORED_PRODUCER_H
diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp
index 3fff928..a6a3eec 100644
--- a/services/surfaceflinger/NativeWindowSurface.cpp
+++ b/services/surfaceflinger/NativeWindowSurface.cpp
@@ -30,7 +30,7 @@
     class NativeWindowSurface final : public surfaceflinger::NativeWindowSurface {
     public:
         explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer)
-              : mSurface(new Surface(producer, /* controlledByApp */ false)) {}
+              : mSurface(sp<Surface>::make(producer, /* controlledByApp */ false)) {}
 
         ~NativeWindowSurface() override = default;
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index a9180d4..f1fd6db 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -29,7 +29,6 @@
 #include <SkBlendMode.h>
 #include <SkRect.h>
 #include <SkSurface.h>
-#include <gui/SurfaceComposerClient.h>
 #include <gui/SurfaceControl.h>
 
 #undef LOG_TAG
@@ -42,19 +41,10 @@
 constexpr int kDigitHeight = 100;
 constexpr int kDigitSpace = 16;
 
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
 constexpr int kBufferHeight = kDigitHeight;
 
-SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
-    constexpr float kFrameRate = 0.f;
-    constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
-    constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
-
-    return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility,
-                                                             kSeamlessness);
-}
-
 } // namespace
 
 SurfaceControlHolder::~SurfaceControlHolder() {
@@ -121,16 +111,10 @@
         drawSegment(Segment::Bottom, left, color, canvas);
 }
 
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
                                                   ui::Transform::RotationFlags rotation,
-                                                  bool showSpinner) -> Buffers {
-    if (number < 0 || number > 1000) return {};
-
-    const auto hundreds = number / 100;
-    const auto tens = (number / 10) % 10;
-    const auto ones = number % 10;
-
-    const size_t loopCount = showSpinner ? 6 : 1;
+                                                  ftl::Flags<Features> features) -> Buffers {
+    const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
 
     Buffers buffers;
     buffers.reserve(loopCount);
@@ -152,13 +136,13 @@
             }
         }();
 
-        sp<GraphicBuffer> buffer =
-                new GraphicBuffer(static_cast<uint32_t>(bufferWidth),
-                                  static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
-                                  1,
-                                  GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
-                                          GRALLOC_USAGE_HW_TEXTURE,
-                                  "RefreshRateOverlayBuffer");
+        const auto kUsageFlags =
+                static_cast<uint64_t>(GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+                                      GRALLOC_USAGE_HW_TEXTURE);
+        sp<GraphicBuffer> buffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+                                                           static_cast<uint32_t>(bufferHeight),
+                                                           HAL_PIXEL_FORMAT_RGBA_8888, 1u,
+                                                           kUsageFlags, "RefreshRateOverlayBuffer");
 
         const status_t bufferStatus = buffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
@@ -169,20 +153,9 @@
         canvas->setMatrix(canvasTransform);
 
         int left = 0;
-        if (hundreds != 0) {
-            drawDigit(hundreds, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        if (tens != 0) {
-            drawDigit(tens, left, color, *canvas);
-        }
-        left += kDigitWidth + kDigitSpace;
-
-        drawDigit(ones, left, color, *canvas);
-        left += kDigitWidth + kDigitSpace;
-
-        if (showSpinner) {
+        drawNumber(displayFps, left, color, *canvas);
+        left += 3 * (kDigitWidth + kDigitSpace);
+        if (features.test(Features::Spinner)) {
             switch (i) {
                 case 0:
                     drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +178,13 @@
             }
         }
 
+        left += kDigitWidth + kDigitSpace;
+
+        if (features.test(Features::RenderRate)) {
+            drawNumber(renderFps, left, color, *canvas);
+        }
+        left += 3 * (kDigitWidth + kDigitSpace);
+
         void* pixels = nullptr;
         buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
 
@@ -219,6 +199,23 @@
     return buffers;
 }
 
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+                                                        SkCanvas& canvas) {
+    if (number < 0 || number >= 1000) return;
+
+    if (number >= 100) {
+        drawDigit(number / 100, left, color, canvas);
+    }
+    left += kDigitWidth + kDigitSpace;
+
+    if (number >= 10) {
+        drawDigit((number / 10) % 10, left, color, canvas);
+    }
+    left += kDigitWidth + kDigitSpace;
+
+    drawDigit(number % 10, left, color, canvas);
+}
+
 std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
     sp<SurfaceControl> surfaceControl =
             SurfaceComposerClient::getDefault()
@@ -228,25 +225,28 @@
     return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
 }
 
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
-      : mFpsRange(fpsRange),
-        mShowSpinner(showSpinner),
-        mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+      : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
     if (!mSurfaceControl) {
         ALOGE("%s: Failed to create buffer state layer", __func__);
         return;
     }
 
-    createTransaction(mSurfaceControl->get())
+    createTransaction()
             .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
             .setTrustedOverlay(mSurfaceControl->get(), true)
             .apply();
 }
 
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
     static const Buffers kNoBuffers;
     if (!mSurfaceControl) return kNoBuffers;
 
+    // avoid caching different render rates if RenderRate is anyway not visible
+    if (!mFeatures.test(Features::RenderRate)) {
+        renderFps = 0_Hz;
+    }
+
     const auto transformHint =
             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
 
@@ -262,21 +262,24 @@
         }
     }();
 
-    createTransaction(mSurfaceControl->get())
-            .setTransform(mSurfaceControl->get(), transform)
-            .apply();
+    createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
 
-    BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+    BufferCache::const_iterator it =
+            mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
     if (it == mBufferCache.end()) {
-        const int minFps = mFpsRange.min.getIntValue();
+        // HWC minFps is not known by the framework in order
+        // to consider lower rates we set minFps to 0.
+        const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
         const int maxFps = mFpsRange.max.getIntValue();
 
-        // Clamp to the range. The current fps may be outside of this range if the display has
-        // changed its set of supported refresh rates.
-        const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+        // Clamp to the range. The current displayFps may be outside of this range if the display
+        // has changed its set of supported refresh rates.
+        const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+        const int renderIntFps = renderFps.getIntValue();
 
         // Ensure non-zero range to avoid division by zero.
-        const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+        const float fpsScale =
+                static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
 
         constexpr SkColor kMinFpsColor = SK_ColorRED;
         constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +295,11 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
-        it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+        auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+                                                mFeatures);
+        it = mBufferCache
+                     .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+                     .first;
     }
 
     return it->second;
@@ -303,10 +309,15 @@
     constexpr int32_t kMaxWidth = 1000;
     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
     const auto height = 2 * width;
-    Rect frame((3 * width) >> 4, height >> 5);
-    frame.offsetBy(width >> 5, height >> 4);
+    Rect frame((5 * width) >> 4, height >> 5);
 
-    createTransaction(mSurfaceControl->get())
+    if (!mFeatures.test(Features::ShowInMiddle)) {
+        frame.offsetBy(width >> 5, height >> 4);
+    } else {
+        frame.offsetBy(width >> 1, height >> 4);
+    }
+
+    createTransaction()
             .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
                        0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
             .setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -314,22 +325,41 @@
 }
 
 void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
-    createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
+    createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
 }
 
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
-    mCurrentFps = fps;
-    const auto buffer = getOrCreateBuffers(fps)[mFrame];
-    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+    mDisplayFps = displayFps;
+    mRenderFps = renderFps;
+    const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
+    createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
 void RefreshRateOverlay::animate() {
-    if (!mShowSpinner || !mCurrentFps) return;
+    if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
 
-    const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+    const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
-    createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
+    createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const {
+    constexpr float kFrameRate = 0.f;
+    constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+    constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+    const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+    SurfaceComposerClient::Transaction transaction;
+    if (isSetByHwc()) {
+        transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator,
+                             layer_state_t::eLayerIsRefreshRateIndicator);
+        // Disable overlay layer caching when refresh rate is updated by the HWC.
+        transaction.setCachingHint(surface, gui::CachingHint::Disabled);
+    }
+    transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+    return transaction;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..0b89b8e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,7 +19,9 @@
 #include <SkColor.h>
 #include <vector>
 
+#include <ftl/flags.h>
 #include <ftl/small_map.h>
+#include <gui/SurfaceComposerClient.h>
 #include <ui/LayerStack.h>
 #include <ui/Size.h>
 #include <ui/Transform.h>
@@ -50,44 +52,61 @@
 
 class RefreshRateOverlay {
 public:
-    RefreshRateOverlay(FpsRange, bool showSpinner);
+    enum class Features {
+        Spinner = 1 << 0,
+        RenderRate = 1 << 1,
+        ShowInMiddle = 1 << 2,
+        SetByHwc = 1 << 3,
+    };
+
+    RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
 
     void setLayerStack(ui::LayerStack);
     void setViewport(ui::Size);
-    void changeRefreshRate(Fps);
+    void changeRefreshRate(Fps, Fps);
     void animate();
+    bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
 
 private:
     using Buffers = std::vector<sp<GraphicBuffer>>;
 
     class SevenSegmentDrawer {
     public:
-        static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+        static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+                            ftl::Flags<Features>);
 
     private:
         enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
 
         static void drawSegment(Segment, int left, SkColor, SkCanvas&);
         static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+        static void drawNumber(int number, int left, SkColor, SkCanvas&);
     };
 
-    const Buffers& getOrCreateBuffers(Fps);
+    const Buffers& getOrCreateBuffers(Fps, Fps);
+
+    SurfaceComposerClient::Transaction createTransaction() const;
 
     struct Key {
-        int fps;
+        int displayFps;
+        int renderFps;
         ui::Transform::RotationFlags flags;
 
-        bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+        bool operator==(Key other) const {
+            return displayFps == other.displayFps && renderFps == other.renderFps &&
+                    flags == other.flags;
+        }
     };
 
     using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
     BufferCache mBufferCache;
 
-    std::optional<Fps> mCurrentFps;
+    std::optional<Fps> mDisplayFps;
+    std::optional<Fps> mRenderFps;
     size_t mFrame = 0;
 
     const FpsRange mFpsRange; // For color interpolation.
-    const bool mShowSpinner;
+    const ftl::Flags<Features> mFeatures;
 
     const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
 };
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index e126931..8f658d5 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -40,6 +40,7 @@
 
 #include "DisplayDevice.h"
 #include "DisplayRenderArea.h"
+#include "FrontEnd/LayerCreationArgs.h"
 #include "Layer.h"
 #include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
@@ -129,12 +130,12 @@
     }
 }
 
-void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+void RegionSamplingThread::addListener(const Rect& samplingArea, uint32_t stopLayerId,
                                        const sp<IRegionSamplingListener>& listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
-    asBinder->linkToDeath(this);
+    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
     std::lock_guard lock(mSamplingMutex);
-    mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener});
+    mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayerId, listener});
 }
 
 void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) {
@@ -276,54 +277,83 @@
 
     const Rect sampledBounds = sampleRegion.bounds();
     constexpr bool kUseIdentityTransform = false;
+    constexpr bool kHintForSeamlessTransition = false;
 
     SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
         return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
-                                         ui::Dataspace::V0_SRGB, kUseIdentityTransform);
+                                         ui::Dataspace::V0_SRGB, kUseIdentityTransform,
+                                         kHintForSeamlessTransition);
     });
 
     std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
 
-    auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
-        bool stopLayerFound = false;
-        auto filterVisitor = [&](Layer* layer) {
-            // We don't want to capture any layers beyond the stop layer
-            if (stopLayerFound) return;
+    auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
+                             const ui::Transform transform, bool& outStopTraversal) -> bool {
+        // Likewise if we just found a stop layer, set the flag and abort
+        for (const auto& [area, stopLayerId, listener] : descriptors) {
+            if (stopLayerId != UNASSIGNED_LAYER_ID && layerId == stopLayerId) {
+                outStopTraversal = true;
+                return false;
+            }
+        }
 
-            // Likewise if we just found a stop layer, set the flag and abort
-            for (const auto& [area, stopLayer, listener] : descriptors) {
-                if (layer == stopLayer.promote().get()) {
-                    stopLayerFound = true;
+        // Compute the layer's position on the screen
+        constexpr bool roundOutwards = true;
+        Rect transformed = transform.transform(bounds, roundOutwards);
+
+        // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
+        Rect ignore;
+        if (!transformed.intersect(sampledBounds, &ignore)) return false;
+
+        // If the layer doesn't intersect a sampling area, skip capturing it
+        bool intersectsAnyArea = false;
+        for (const auto& [area, stopLayer, listener] : descriptors) {
+            if (transformed.intersect(area, &ignore)) {
+                intersectsAnyArea = true;
+                listeners.insert(listener);
+            }
+        }
+        if (!intersectsAnyArea) return false;
+
+        ALOGV("Traversing [%s] [%d, %d, %d, %d]", layerName, bounds.left, bounds.top, bounds.right,
+              bounds.bottom);
+
+        return true;
+    };
+
+    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
+    if (mFlinger.mLayerLifecycleManagerEnabled) {
+        auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
+                            bool& outStopTraversal) -> bool {
+            const Rect bounds =
+                    frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+                                                          snapshot.transparentRegionHint);
+            const ui::Transform transform = snapshot.geomLayerTransform;
+            return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+                                 outStopTraversal);
+        };
+        getLayerSnapshots =
+                mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+                                                         filterFn);
+    } else {
+        auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
+            bool stopLayerFound = false;
+            auto filterVisitor = [&](Layer* layer) {
+                // We don't want to capture any layers beyond the stop layer
+                if (stopLayerFound) return;
+
+                if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
+                                   Rect(layer->getBounds()), layer->getTransform(),
+                                   stopLayerFound)) {
                     return;
                 }
-            }
-
-            // Compute the layer's position on the screen
-            const Rect bounds = Rect(layer->getBounds());
-            const ui::Transform transform = layer->getTransform();
-            constexpr bool roundOutwards = true;
-            Rect transformed = transform.transform(bounds, roundOutwards);
-
-            // If this layer doesn't intersect with the larger sampledBounds, skip capturing it
-            Rect ignore;
-            if (!transformed.intersect(sampledBounds, &ignore)) return;
-
-            // If the layer doesn't intersect a sampling area, skip capturing it
-            bool intersectsAnyArea = false;
-            for (const auto& [area, stopLayer, listener] : descriptors) {
-                if (transformed.intersect(area, &ignore)) {
-                    intersectsAnyArea = true;
-                    listeners.insert(listener);
-                }
-            }
-            if (!intersectsAnyArea) return;
-
-            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
-                  bounds.top, bounds.right, bounds.bottom);
-            visitor(layer);
+                visitor(layer);
+            };
+            mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {},
+                                                filterVisitor);
         };
-        mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor);
-    };
+        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    }
 
     std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
     if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -333,8 +363,8 @@
         const uint32_t usage =
                 GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
         sp<GraphicBuffer> graphicBuffer =
-                new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(),
-                                  PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
+                sp<GraphicBuffer>::make(sampledBounds.getWidth(), sampledBounds.getHeight(),
+                                        PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
         const status_t bufferStatus = graphicBuffer->initCheck();
         LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d",
                             bufferStatus);
@@ -348,7 +378,7 @@
     constexpr bool kGrayscale = false;
 
     if (const auto fenceResult =
-                mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+                mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
                                              kRegionSampling, kGrayscale, nullptr)
                         .get();
         fenceResult.ok()) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 686b4b1..e8c891e 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -26,6 +26,7 @@
 
 #include <chrono>
 #include <condition_variable>
+#include <cstdint>
 #include <mutex>
 #include <thread>
 #include <unordered_map>
@@ -36,7 +37,6 @@
 namespace android {
 
 class Layer;
-class Scheduler;
 class SurfaceFlinger;
 struct SamplingOffsetCallback;
 
@@ -73,7 +73,7 @@
 
     // Add a listener to receive luma notifications. The luma reported via listener will
     // report the median luma for the layers under the stopLayerHandle, in the samplingArea region.
-    void addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
+    void addListener(const Rect& samplingArea, uint32_t stopLayerId,
                      const sp<IRegionSamplingListener>& listener);
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
@@ -87,7 +87,7 @@
 private:
     struct Descriptor {
         Rect area = Rect::EMPTY_RECT;
-        wp<Layer> stopLayer;
+        uint32_t stopLayerId;
         sp<IRegionSamplingListener> listener;
     };
 
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 387364c..71b85bd 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -25,14 +25,29 @@
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
-               const Rect& layerStackRect, bool allowSecureLayers = false,
+               bool hintForSeamlessTransition, bool allowSecureLayers = false,
                RotationFlags rotation = ui::Transform::ROT_0)
           : mAllowSecureLayers(allowSecureLayers),
             mReqSize(reqSize),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
             mRotationFlags(rotation),
-            mLayerStackSpaceRect(layerStackRect) {}
+            mHintForSeamlessTransition(hintForSeamlessTransition) {}
+
+    static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
+            std::function<void(const LayerVector::Visitor&)> traverseLayers) {
+        return [traverseLayers = std::move(traverseLayers)]() {
+            std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+            traverseLayers([&](Layer* layer) {
+                // Layer::prepareClientComposition uses the layer's snapshot to populate the
+                // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings
+                // are generated with the layer's current buffer and geometry.
+                layer->updateSnapshot(true /* updateGeometry */);
+                layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
+            });
+            return layers;
+        };
+    }
 
     virtual ~RenderArea() = default;
 
@@ -43,20 +58,10 @@
     // blacked out / skipped when rendered to an insecure render area.
     virtual bool isSecure() const = 0;
 
-    // Returns true if the otherwise disabled layer filtering should be
-    // enabled when rendering to this render area.
-    virtual bool needsFiltering() const = 0;
-
     // Returns the transform to be applied on layers to transform them into
     // the logical render area.
     virtual const ui::Transform& getTransform() const = 0;
 
-    // Returns the size of the logical render area.  Layers are clipped to the
-    // logical render area.
-    virtual int getWidth() const = 0;
-    virtual int getHeight() const = 0;
-    virtual Rect getBounds() const = 0;
-
     // Returns the source crop of the render area.  The source crop defines
     // how layers are projected from the logical render area onto the physical
     // render area.  It can be larger than the logical render area.  It can
@@ -83,13 +88,14 @@
 
     virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
 
-    // Returns the source display viewport.
-    const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; }
-
     // If this is a LayerRenderArea, return the root layer of the
     // capture operation.
     virtual sp<Layer> getParentLayer() const { return nullptr; }
 
+    // Returns whether the render result may be used for system animations that
+    // must preserve the exact colors of the display.
+    bool getHintForSeamlessTransition() const { return mHintForSeamlessTransition; }
+
 protected:
     const bool mAllowSecureLayers;
 
@@ -98,7 +104,7 @@
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
     const RotationFlags mRotationFlags;
-    const Rect mLayerStackSpaceRect;
+    const bool mHintForSeamlessTransition;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 8df9ba5..d5d8688 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -18,6 +18,7 @@
         "libbase",
         "libcutils",
         "liblog",
+        "libui",
         "libutils",
     ],
 }
@@ -39,6 +40,7 @@
     name: "libscheduler",
     defaults: ["libscheduler_defaults"],
     srcs: [
+        "src/PresentLatencyTracker.cpp",
         "src/Timer.cpp",
     ],
     local_include_dirs: ["include"],
@@ -50,6 +52,7 @@
     test_suites: ["device-tests"],
     defaults: ["libscheduler_defaults"],
     srcs: [
+        "tests/PresentLatencyTrackerTest.cpp",
         "tests/TimerTest.cpp",
     ],
     static_libs: [
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
deleted file mode 100644
index 4af1f5c..0000000
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ /dev/null
@@ -1,202 +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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "DispSyncSource.h"
-
-#include <android-base/stringprintf.h>
-#include <utils/Trace.h>
-#include <mutex>
-
-#include "EventThread.h"
-#include "VSyncTracker.h"
-#include "VsyncController.h"
-
-namespace android::scheduler {
-using base::StringAppendF;
-using namespace std::chrono_literals;
-
-class CallbackRepeater {
-public:
-    CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
-                     std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
-                     std::chrono::nanoseconds notBefore)
-          : mName(name),
-            mCallback(cb),
-            mRegistration(dispatch,
-                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
-                                    std::placeholders::_2, std::placeholders::_3),
-                          mName),
-            mStarted(false),
-            mWorkDuration(workDuration),
-            mReadyDuration(readyDuration),
-            mLastCallTime(notBefore) {}
-
-    ~CallbackRepeater() {
-        std::lock_guard lock(mMutex);
-        mRegistration.cancel();
-    }
-
-    void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
-        std::lock_guard lock(mMutex);
-        mStarted = true;
-        mWorkDuration = workDuration;
-        mReadyDuration = readyDuration;
-
-        auto const scheduleResult =
-                mRegistration.schedule({.workDuration = mWorkDuration.count(),
-                                        .readyDuration = mReadyDuration.count(),
-                                        .earliestVsync = mLastCallTime.count()});
-        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
-    }
-
-    void stop() {
-        std::lock_guard lock(mMutex);
-        LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
-        mStarted = false;
-        mRegistration.cancel();
-    }
-
-    void dump(std::string& result) const {
-        std::lock_guard lock(mMutex);
-        const auto relativeLastCallTime =
-                mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
-        StringAppendF(&result, "\t%s: ", mName.c_str());
-        StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
-                      mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
-        StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
-                      mStarted ? "running" : "stopped");
-    }
-
-private:
-    void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
-        {
-            std::lock_guard lock(mMutex);
-            mLastCallTime = std::chrono::nanoseconds(vsyncTime);
-        }
-
-        mCallback(vsyncTime, wakeupTime, readyTime);
-
-        {
-            std::lock_guard lock(mMutex);
-            if (!mStarted) {
-                return;
-            }
-            auto const scheduleResult =
-                    mRegistration.schedule({.workDuration = mWorkDuration.count(),
-                                            .readyDuration = mReadyDuration.count(),
-                                            .earliestVsync = vsyncTime});
-            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
-        }
-    }
-
-    const std::string mName;
-    scheduler::VSyncDispatch::Callback mCallback;
-
-    mutable std::mutex mMutex;
-    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
-    bool mStarted GUARDED_BY(mMutex) = false;
-    std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
-    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
-    std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
-};
-
-DispSyncSource::DispSyncSource(VSyncDispatch& vSyncDispatch, VSyncTracker& vSyncTracker,
-                               std::chrono::nanoseconds workDuration,
-                               std::chrono::nanoseconds readyDuration, bool traceVsync,
-                               const char* name)
-      : mName(name),
-        mValue(base::StringPrintf("VSYNC-%s", name), 0),
-        mTraceVsync(traceVsync),
-        mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
-        mVSyncTracker(vSyncTracker),
-        mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
-        mReadyDuration(readyDuration) {
-    mCallbackRepeater =
-            std::make_unique<CallbackRepeater>(vSyncDispatch,
-                                               std::bind(&DispSyncSource::onVsyncCallback, this,
-                                                         std::placeholders::_1,
-                                                         std::placeholders::_2,
-                                                         std::placeholders::_3),
-                                               name, workDuration, readyDuration,
-                                               std::chrono::steady_clock::now().time_since_epoch());
-}
-
-DispSyncSource::~DispSyncSource() = default;
-
-void DispSyncSource::setVSyncEnabled(bool enable) {
-    std::lock_guard lock(mVsyncMutex);
-    if (enable) {
-        mCallbackRepeater->start(mWorkDuration, mReadyDuration);
-        // ATRACE_INT(mVsyncOnLabel.c_str(), 1);
-    } else {
-        mCallbackRepeater->stop();
-        // ATRACE_INT(mVsyncOnLabel.c_str(), 0);
-    }
-    mEnabled = enable;
-}
-
-void DispSyncSource::setCallback(VSyncSource::Callback* callback) {
-    std::lock_guard lock(mCallbackMutex);
-    mCallback = callback;
-}
-
-void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
-                                 std::chrono::nanoseconds readyDuration) {
-    std::lock_guard lock(mVsyncMutex);
-    mWorkDuration = workDuration;
-    mReadyDuration = readyDuration;
-
-    // If we're not enabled, we don't need to mess with the listeners
-    if (!mEnabled) {
-        return;
-    }
-
-    mCallbackRepeater->start(mWorkDuration, mReadyDuration);
-}
-
-void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
-                                     nsecs_t readyTime) {
-    VSyncSource::Callback* callback;
-    {
-        std::lock_guard lock(mCallbackMutex);
-        callback = mCallback;
-    }
-
-    if (mTraceVsync) {
-        mValue = (mValue + 1) % 2;
-    }
-
-    if (callback != nullptr) {
-        callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});
-    }
-}
-
-VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const {
-    std::lock_guard lock(mVsyncMutex);
-    nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom(
-            systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
-    nsecs_t deadline = expectedPresentationTime - mReadyDuration.count();
-    return {expectedPresentationTime, deadline};
-}
-
-void DispSyncSource::dump(std::string& result) const {
-    std::lock_guard lock(mVsyncMutex);
-    StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
deleted file mode 100644
index edcd3ac..0000000
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ /dev/null
@@ -1,69 +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 <mutex>
-#include <string>
-
-#include "EventThread.h"
-#include "TracedOrdinal.h"
-#include "VSyncDispatch.h"
-
-namespace android::scheduler {
-class CallbackRepeater;
-class VSyncTracker;
-
-class DispSyncSource final : public VSyncSource {
-public:
-    DispSyncSource(VSyncDispatch& vSyncDispatch, VSyncTracker& vSyncTracker,
-                   std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
-                   bool traceVsync, const char* name);
-
-    ~DispSyncSource() override;
-
-    // The following methods are implementation of VSyncSource.
-    const char* getName() const override { return mName; }
-    void setVSyncEnabled(bool enable) override;
-    void setCallback(VSyncSource::Callback* callback) override;
-    void setDuration(std::chrono::nanoseconds workDuration,
-                     std::chrono::nanoseconds readyDuration) override;
-    VSyncData getLatestVSyncData() const override;
-
-    void dump(std::string&) const override;
-
-private:
-    void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
-
-    const char* const mName;
-    TracedOrdinal<int> mValue;
-
-    const bool mTraceVsync;
-    const std::string mVsyncOnLabel;
-
-    const VSyncTracker& mVSyncTracker;
-
-    std::unique_ptr<CallbackRepeater> mCallbackRepeater;
-
-    std::mutex mCallbackMutex;
-    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
-
-    mutable std::mutex mVsyncMutex;
-    TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex);
-    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex);
-    bool mEnabled GUARDED_BY(mVsyncMutex) = false;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 639ba5a..281b0ae 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -28,6 +28,7 @@
 #include <cstdint>
 #include <optional>
 #include <type_traits>
+#include <utility>
 
 #include <android-base/stringprintf.h>
 
@@ -41,8 +42,11 @@
 #include <utils/Errors.h>
 #include <utils/Trace.h>
 
+#include <scheduler/VsyncConfig.h>
 #include "DisplayHardware/DisplayMode.h"
 #include "FrameTimeline.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
 
 #include "EventThread.h"
 
@@ -124,12 +128,12 @@
     return event;
 }
 
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
     DisplayEventReceiver::Event event;
-    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
-                    systemTime()};
-    event.modeChange.modeId = mode->getId().value();
-    event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+    event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+                    mode.modePtr->getPhysicalDisplayId(), systemTime()};
+    event.modeChange.modeId = mode.modePtr->getId().value();
+    event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
     return event;
 }
 
@@ -157,9 +161,9 @@
 
 } // namespace
 
-EventThreadConnection::EventThreadConnection(
-        EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback,
-        ISurfaceComposer::EventRegistrationFlags eventRegistration)
+EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
+                                             ResyncCallback resyncCallback,
+                                             EventRegistrationFlags eventRegistration)
       : resyncCallback(std::move(resyncCallback)),
         mOwnerUid(callingUid),
         mEventRegistration(eventRegistration),
@@ -173,7 +177,7 @@
 
 void EventThreadConnection::onFirstRef() {
     // NOTE: mEventThread doesn't hold a strong reference on us
-    mEventThread->registerDisplayEventConnection(this);
+    mEventThread->registerDisplayEventConnection(sp<EventThreadConnection>::fromExisting(this));
 }
 
 binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
@@ -188,20 +192,22 @@
 }
 
 binder::Status EventThreadConnection::setVsyncRate(int rate) {
-    mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this);
+    mEventThread->setVsyncRate(static_cast<uint32_t>(rate),
+                               sp<EventThreadConnection>::fromExisting(this));
     return binder::Status::ok();
 }
 
 binder::Status EventThreadConnection::requestNextVsync() {
     ATRACE_CALL();
-    mEventThread->requestNextVsync(this);
+    mEventThread->requestNextVsync(sp<EventThreadConnection>::fromExisting(this));
     return binder::Status::ok();
 }
 
 binder::Status EventThreadConnection::getLatestVsyncEventData(
         ParcelableVsyncEventData* outVsyncEventData) {
     ATRACE_CALL();
-    outVsyncEventData->vsync = mEventThread->getLatestVsyncEventData(this);
+    outVsyncEventData->vsync =
+            mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this));
     return binder::Status::ok();
 }
 
@@ -233,23 +239,24 @@
 
 namespace impl {
 
-EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule,
                          android::frametimeline::TokenManager* tokenManager,
-                         InterceptVSyncsCallback interceptVSyncsCallback,
                          ThrottleVsyncCallback throttleVsyncCallback,
-                         GetVsyncPeriodFunction getVsyncPeriodFunction)
-      : mVSyncSource(std::move(vsyncSource)),
+                         GetVsyncPeriodFunction getVsyncPeriodFunction,
+                         std::chrono::nanoseconds workDuration,
+                         std::chrono::nanoseconds readyDuration)
+      : mThreadName(name),
+        mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0),
+        mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
+        mReadyDuration(readyDuration),
+        mVsyncSchedule(std::move(vsyncSchedule)),
+        mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name),
         mTokenManager(tokenManager),
-        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
         mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
-        mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)),
-        mThreadName(mVSyncSource->getName()) {
-
+        mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) {
     LOG_ALWAYS_FATAL_IF(getVsyncPeriodFunction == nullptr,
             "getVsyncPeriodFunction must not be null");
 
-    mVSyncSource->setCallback(this);
-
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
         std::unique_lock<std::mutex> lock(mMutex);
         threadMain(lock);
@@ -271,8 +278,6 @@
 }
 
 EventThread::~EventThread() {
-    mVSyncSource->setCallback(nullptr);
-
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = State::Quit;
@@ -284,15 +289,19 @@
 void EventThread::setDuration(std::chrono::nanoseconds workDuration,
                               std::chrono::nanoseconds readyDuration) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mVSyncSource->setDuration(workDuration, readyDuration);
+    mWorkDuration = workDuration;
+    mReadyDuration = readyDuration;
+
+    mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
+                               .readyDuration = mReadyDuration.count(),
+                               .earliestVsync = mLastVsyncCallbackTime.ns()});
 }
 
 sp<EventThreadConnection> EventThread::createEventConnection(
-        ResyncCallback resyncCallback,
-        ISurfaceComposer::EventRegistrationFlags eventRegistration) const {
-    return new EventThreadConnection(const_cast<EventThread*>(this),
-                                     IPCThreadState::self()->getCallingUid(),
-                                     std::move(resyncCallback), eventRegistration);
+        ResyncCallback resyncCallback, EventRegistrationFlags eventRegistration) const {
+    return sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
+                                           IPCThreadState::self()->getCallingUid(),
+                                           std::move(resyncCallback), eventRegistration);
 }
 
 status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -360,43 +369,35 @@
     VsyncEventData vsyncEventData;
     nsecs_t frameInterval = mGetVsyncPeriodFunction(connection->mOwnerUid);
     vsyncEventData.frameInterval = frameInterval;
-    VSyncSource::VSyncData vsyncData;
-    {
+    const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
         std::lock_guard<std::mutex> lock(mMutex);
-        vsyncData = mVSyncSource->getLatestVSyncData();
-    }
+        const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
+                systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
+        return {vsyncTime, vsyncTime - mReadyDuration.count()};
+    }();
     generateFrameTimeline(vsyncEventData, frameInterval, systemTime(SYSTEM_TIME_MONOTONIC),
-                          vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp);
+                          presentTime, deadline);
     return vsyncEventData;
 }
 
-void EventThread::onScreenReleased() {
+void EventThread::enableSyntheticVsync(bool enable) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mVSyncState || mVSyncState->synthetic) {
+    if (!mVSyncState || mVSyncState->synthetic == enable) {
         return;
     }
 
-    mVSyncState->synthetic = true;
+    mVSyncState->synthetic = enable;
     mCondition.notify_all();
 }
 
-void EventThread::onScreenAcquired() {
+void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mVSyncState || !mVSyncState->synthetic) {
-        return;
-    }
-
-    mVSyncState->synthetic = false;
-    mCondition.notify_all();
-}
-
-void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime);
 
     LOG_FATAL_IF(!mVSyncState);
-    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       vsyncData.expectedPresentationTime,
-                                       vsyncData.deadlineTimestamp));
+    mVsyncTracer = (mVsyncTracer + 1) % 2;
+    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, wakeupTime, ++mVSyncState->count,
+                                       vsyncTime, readyTime));
     mCondition.notify_all();
 }
 
@@ -407,7 +408,7 @@
     mCondition.notify_all();
 }
 
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     mPendingEvents.push_back(makeModeChanged(mode));
@@ -442,21 +443,13 @@
             event = mPendingEvents.front();
             mPendingEvents.pop_front();
 
-            switch (event->header.type) {
-                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
-                    if (event->hotplug.connected && !mVSyncState) {
-                        mVSyncState.emplace(event->header.displayId);
-                    } else if (!event->hotplug.connected && mVSyncState &&
-                               mVSyncState->displayId == event->header.displayId) {
-                        mVSyncState.reset();
-                    }
-                    break;
-
-                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
-                    if (mInterceptVSyncsCallback) {
-                        mInterceptVSyncsCallback(event->header.timestamp);
-                    }
-                    break;
+            if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
+                if (event->hotplug.connected && !mVSyncState) {
+                    mVSyncState.emplace(event->header.displayId);
+                } else if (!event->hotplug.connected && mVSyncState &&
+                           mVSyncState->displayId == event->header.displayId) {
+                    mVSyncState.reset();
+                }
             }
         }
 
@@ -466,12 +459,12 @@
         auto it = mDisplayEventConnections.begin();
         while (it != mDisplayEventConnections.end()) {
             if (const auto connection = it->promote()) {
-                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
-
                 if (event && shouldConsumeEvent(*event, connection)) {
                     consumers.push_back(connection);
                 }
 
+                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
+
                 ++it;
             } else {
                 it = mDisplayEventConnections.erase(it);
@@ -483,25 +476,24 @@
             consumers.clear();
         }
 
-        State nextState;
         if (mVSyncState && vsyncRequested) {
-            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+            mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
         } else {
             ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
-            nextState = State::Idle;
+            mState = State::Idle;
         }
 
-        if (mState != nextState) {
-            if (mState == State::VSync) {
-                mVSyncSource->setVSyncEnabled(false);
-            } else if (nextState == State::VSync) {
-                mVSyncSource->setVSyncEnabled(true);
-            }
-
-            mState = nextState;
+        if (mState == State::VSync) {
+            const auto scheduleResult =
+                    mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
+                                                 .readyDuration = mReadyDuration.count(),
+                                                 .earliestVsync = mLastVsyncCallbackTime.ns()});
+            LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
+        } else {
+            mVsyncRegistration.cancel();
         }
 
-        if (event) {
+        if (!mPendingEvents.empty()) {
             continue;
         }
 
@@ -516,15 +508,6 @@
             if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
                 if (mState == State::VSync) {
                     ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
-                    std::string debugInfo = "VsyncSource debug info:\n";
-                    mVSyncSource->dump(debugInfo);
-                    // Log the debug info line-by-line to avoid logcat overflow
-                    auto pos = debugInfo.find('\n');
-                    while (pos != std::string::npos) {
-                        ALOGW("%s", debugInfo.substr(0, pos).c_str());
-                        debugInfo = debugInfo.substr(pos + 1);
-                        pos = debugInfo.find('\n');
-                    }
                 }
 
                 LOG_FATAL_IF(!mVSyncState);
@@ -537,11 +520,20 @@
             }
         }
     }
+    // cancel any pending vsync event before exiting
+    mVsyncRegistration.cancel();
 }
 
 bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
                                      const sp<EventThreadConnection>& connection) const {
-    const auto throttleVsync = [&] {
+    const auto throttleVsync = [&]() REQUIRES(mMutex) {
+        const auto& vsyncData = event.vsync.vsyncData;
+        if (connection->frameRate.isValid()) {
+            return !mVsyncSchedule->getTracker()
+                            .isVSyncInPhase(vsyncData.preferredExpectedPresentationTime(),
+                                            connection->frameRate);
+        }
+
         return mThrottleVsyncCallback &&
                 mThrottleVsyncCallback(event.vsync.vsyncData.preferredExpectedPresentationTime(),
                                        connection->mOwnerUid);
@@ -553,7 +545,7 @@
 
         case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: {
             return connection->mEventRegistration.test(
-                    ISurfaceComposer::EventRegistration::modeChanged);
+                    gui::ISurfaceComposer::EventRegistration::modeChanged);
         }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
@@ -586,7 +578,7 @@
             [[fallthrough]];
         case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH:
             return connection->mEventRegistration.test(
-                    ISurfaceComposer::EventRegistration::frameRateOverride);
+                    gui::ISurfaceComposer::EventRegistration::frameRateOverride);
 
         default:
             return false;
@@ -606,25 +598,57 @@
                                         nsecs_t timestamp,
                                         nsecs_t preferredExpectedPresentationTime,
                                         nsecs_t preferredDeadlineTimestamp) const {
+    uint32_t currentIndex = 0;
     // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
-    for (int64_t multiplier = -VsyncEventData::kFrameTimelinesLength + 1, currentIndex = 0;
-         currentIndex < VsyncEventData::kFrameTimelinesLength; multiplier++) {
+    for (int64_t multiplier = -VsyncEventData::kFrameTimelinesCapacity + 1;
+         currentIndex < VsyncEventData::kFrameTimelinesCapacity; multiplier++) {
         nsecs_t deadlineTimestamp = preferredDeadlineTimestamp + multiplier * frameInterval;
-        // Valid possible frame timelines must have future values.
-        if (deadlineTimestamp > timestamp) {
-            if (multiplier == 0) {
-                outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
-            }
-            nsecs_t expectedPresentationTime =
-                    preferredExpectedPresentationTime + multiplier * frameInterval;
-            outVsyncEventData.frameTimelines[currentIndex] =
-                    {.vsyncId =
-                             generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
-                     .deadlineTimestamp = deadlineTimestamp,
-                     .expectedPresentationTime = expectedPresentationTime};
-            currentIndex++;
+        // Valid possible frame timelines must have future values, so find a later frame timeline.
+        if (deadlineTimestamp <= timestamp) {
+            continue;
         }
+
+        nsecs_t expectedPresentationTime =
+                preferredExpectedPresentationTime + multiplier * frameInterval;
+        if (expectedPresentationTime >= preferredExpectedPresentationTime +
+                    scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()) {
+            if (currentIndex == 0) {
+                ALOGW("%s: Expected present time is too far in the future but no timelines are "
+                      "valid. preferred EPT=%" PRId64 ", Calculated EPT=%" PRId64
+                      ", multiplier=%" PRId64 ", frameInterval=%" PRId64 ", threshold=%" PRId64,
+                      __func__, preferredExpectedPresentationTime, expectedPresentationTime,
+                      multiplier, frameInterval,
+                      static_cast<int64_t>(
+                              scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+            }
+            break;
+        }
+
+        if (multiplier == 0) {
+            outVsyncEventData.preferredFrameTimelineIndex = currentIndex;
+        }
+
+        outVsyncEventData.frameTimelines[currentIndex] =
+                {.vsyncId = generateToken(timestamp, deadlineTimestamp, expectedPresentationTime),
+                 .deadlineTimestamp = deadlineTimestamp,
+                 .expectedPresentationTime = expectedPresentationTime};
+        currentIndex++;
     }
+
+    if (currentIndex == 0) {
+        ALOGW("%s: No timelines are valid. preferred EPT=%" PRId64 ", frameInterval=%" PRId64
+              ", threshold=%" PRId64,
+              __func__, preferredExpectedPresentationTime, frameInterval,
+              static_cast<int64_t>(scheduler::VsyncConfig::kEarlyLatchMaxThreshold.count()));
+        outVsyncEventData.frameTimelines[currentIndex] =
+                {.vsyncId = generateToken(timestamp, preferredDeadlineTimestamp,
+                                          preferredExpectedPresentationTime),
+                 .deadlineTimestamp = preferredDeadlineTimestamp,
+                 .expectedPresentationTime = preferredExpectedPresentationTime};
+        currentIndex++;
+    }
+
+    outVsyncEventData.frameTimelinesLength = currentIndex;
 }
 
 void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
@@ -667,6 +691,12 @@
         StringAppendF(&result, "none\n");
     }
 
+    const auto relativeLastCallTime =
+            ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now());
+    StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
+                  mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f);
+    StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime);
+
     StringAppendF(&result, "  pending events (count=%zu):\n", mPendingEvents.size());
     for (const auto& event : mPendingEvents) {
         StringAppendF(&result, "    %s\n", toString(event).c_str());
@@ -678,6 +708,7 @@
             StringAppendF(&result, "    %s\n", toString(*connection).c_str());
         }
     }
+    result += '\n';
 }
 
 const char* EventThread::toCString(State state) {
@@ -693,6 +724,36 @@
     }
 }
 
+void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) {
+    // Hold onto the old registration until after releasing the mutex to avoid deadlock.
+    scheduler::VSyncCallbackRegistration oldRegistration =
+            onNewVsyncScheduleInternal(std::move(schedule));
+}
+
+scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal(
+        std::shared_ptr<scheduler::VsyncSchedule> schedule) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled;
+    mVsyncSchedule = std::move(schedule);
+    auto oldRegistration =
+            std::exchange(mVsyncRegistration,
+                          scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(),
+                                                               createDispatchCallback(),
+                                                               mThreadName));
+    if (reschedule) {
+        mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
+                                     .readyDuration = mReadyDuration.count(),
+                                     .earliestVsync = mLastVsyncCallbackTime.ns()});
+    }
+    return oldRegistration;
+}
+
+scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() {
+    return [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
+        onVsync(vsyncTime, wakeupTime, readyTime);
+    };
+}
+
 } // namespace impl
 } // namespace android
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index adb96fd..684745b 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
 #include <sys/types.h>
 #include <utils/Errors.h>
 
+#include <scheduler/FrameRateMode.h>
 #include <condition_variable>
 #include <cstdint>
 #include <deque>
@@ -32,6 +33,9 @@
 #include <vector>
 
 #include "DisplayHardware/DisplayMode.h"
+#include "TracedOrdinal.h"
+#include "VSyncDispatch.h"
+#include "VsyncSchedule.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -63,36 +67,10 @@
     // Subsequent values are periods.
 };
 
-class VSyncSource {
-public:
-    class VSyncData {
-    public:
-        nsecs_t expectedPresentationTime;
-        nsecs_t deadlineTimestamp;
-    };
-
-    class Callback {
-    public:
-        virtual ~Callback() {}
-        virtual void onVSyncEvent(nsecs_t when, VSyncData vsyncData) = 0;
-    };
-
-    virtual ~VSyncSource() {}
-
-    virtual const char* getName() const = 0;
-    virtual void setVSyncEnabled(bool enable) = 0;
-    virtual void setCallback(Callback* callback) = 0;
-    virtual void setDuration(std::chrono::nanoseconds workDuration,
-                             std::chrono::nanoseconds readyDuration) = 0;
-    virtual VSyncData getLatestVSyncData() const = 0;
-
-    virtual void dump(std::string& result) const = 0;
-};
-
 class EventThreadConnection : public gui::BnDisplayEventConnection {
 public:
     EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
-                          ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
+                          EventRegistrationFlags eventRegistration = {});
     virtual ~EventThreadConnection();
 
     virtual status_t postEvent(const DisplayEventReceiver::Event& event);
@@ -107,7 +85,10 @@
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
     const uid_t mOwnerUid;
-    const ISurfaceComposer::EventRegistrationFlags mEventRegistration;
+    const EventRegistrationFlags mEventRegistration;
+
+    /** The frame rate set to the attached choreographer. */
+    Fps frameRate;
 
 private:
     virtual void onFirstRef();
@@ -123,19 +104,15 @@
     virtual ~EventThread();
 
     virtual sp<EventThreadConnection> createEventConnection(
-            ResyncCallback,
-            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const = 0;
+            ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0;
 
-    // called before the screen is turned off from main thread
-    virtual void onScreenReleased() = 0;
-
-    // called after the screen is turned on from main thread
-    virtual void onScreenAcquired() = 0;
+    // Feed clients with fake VSYNC, e.g. while the display is off.
+    virtual void enableSyntheticVsync(bool) = 0;
 
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     // called when SF changes the active mode and apps needs to be notified about the change
-    virtual void onModeChanged(DisplayModePtr) = 0;
+    virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
 
     // called when SF updates the Frame Rate Override list
     virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -156,23 +133,24 @@
 
     // Retrieves the number of event connections tracked by this EventThread.
     virtual size_t getEventThreadConnectionCount() = 0;
+
+    virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
 };
 
 namespace impl {
 
-class EventThread : public android::EventThread, private VSyncSource::Callback {
+class EventThread : public android::EventThread {
 public:
-    using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
     using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
     using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>;
 
-    EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
-                ThrottleVsyncCallback, GetVsyncPeriodFunction);
+    EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>,
+                frametimeline::TokenManager*, ThrottleVsyncCallback, GetVsyncPeriodFunction,
+                std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
-            ResyncCallback,
-            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) const override;
+            ResyncCallback, EventRegistrationFlags eventRegistration = {}) const override;
 
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
@@ -180,15 +158,11 @@
     VsyncEventData getLatestVsyncEventData(
             const sp<EventThreadConnection>& connection) const override;
 
-    // called before the screen is turned off from main thread
-    void onScreenReleased() override;
-
-    // called after the screen is turned on from main thread
-    void onScreenAcquired() override;
+    void enableSyntheticVsync(bool) override;
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onModeChanged(DisplayModePtr) override;
+    void onModeChanged(const scheduler::FrameRateMode&) override;
 
     void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
                                      std::vector<FrameRateOverride> overrides) override;
@@ -200,6 +174,8 @@
 
     size_t getEventThreadConnectionCount() override;
 
+    void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override EXCLUDES(mMutex);
+
 private:
     friend EventThreadTest;
 
@@ -215,8 +191,7 @@
     void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection)
             REQUIRES(mMutex);
 
-    // Implements VSyncSource::Callback
-    void onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) override;
+    void onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime);
 
     int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
                           nsecs_t expectedPresentationTime) const;
@@ -224,13 +199,24 @@
                                nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime,
                                nsecs_t preferredDeadlineTimestamp) const;
 
-    const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
+    scheduler::VSyncDispatch::Callback createDispatchCallback();
+
+    // Returns the old registration so it can be destructed outside the lock to
+    // avoid deadlock.
+    scheduler::VSyncCallbackRegistration onNewVsyncScheduleInternal(
+            std::shared_ptr<scheduler::VsyncSchedule>) EXCLUDES(mMutex);
+
+    const char* const mThreadName;
+    TracedOrdinal<int> mVsyncTracer;
+    TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex);
+    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex);
+    std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule GUARDED_BY(mMutex);
+    TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now();
+    scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
     frametimeline::TokenManager* const mTokenManager;
 
-    const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const ThrottleVsyncCallback mThrottleVsyncCallback;
     const GetVsyncPeriodFunction mGetVsyncPeriodFunction;
-    const char* const mThreadName;
 
     std::thread mThread;
     mutable std::mutex mMutex;
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
index c233455..cb9bfe9 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.cpp
@@ -54,10 +54,9 @@
 std::vector<FrameRateOverride> FrameRateOverrideMappings::getAllFrameRateOverrides(
         bool supportsFrameRateOverrideByContent) {
     std::lock_guard lock(mFrameRateOverridesLock);
+
     std::vector<FrameRateOverride> overrides;
-    overrides.reserve(std::max({mFrameRateOverridesFromGameManager.size(),
-                                mFrameRateOverridesFromBackdoor.size(),
-                                mFrameRateOverridesByContent.size()}));
+    overrides.reserve(maxOverridesCount());
 
     for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
         overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
@@ -83,28 +82,34 @@
     return overrides;
 }
 
-void FrameRateOverrideMappings::dump(std::string& result) const {
-    using base::StringAppendF;
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
 
     std::lock_guard lock(mFrameRateOverridesLock);
 
-    StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
-    for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
-        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
-    }
-    StringAppendF(&result, "}\n");
+    const bool hasOverrides = maxOverridesCount() > 0;
+    dumper.dump("FrameRateOverrides"sv, hasOverrides ? ""sv : "none"sv);
 
-    StringAppendF(&result, "Frame Rate Overrides (GameManager): {");
-    for (const auto& [uid, frameRate] : mFrameRateOverridesFromGameManager) {
-        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
-    }
-    StringAppendF(&result, "}\n");
+    if (!hasOverrides) return;
 
-    StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
-    for (const auto& [uid, frameRate] : mFrameRateOverridesByContent) {
-        StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+    dump(dumper, "setFrameRate"sv, mFrameRateOverridesByContent);
+    dump(dumper, "GameManager"sv, mFrameRateOverridesFromGameManager);
+    dump(dumper, "Backdoor"sv, mFrameRateOverridesFromBackdoor);
+}
+
+void FrameRateOverrideMappings::dump(utils::Dumper& dumper, std::string_view name,
+                                     const UidToFrameRateOverride& overrides) const {
+    if (overrides.empty()) return;
+
+    utils::Dumper::Indent indent(dumper);
+    dumper.dump(name);
+    {
+        utils::Dumper::Indent indent(dumper);
+        for (const auto& [uid, frameRate] : overrides) {
+            using namespace std::string_view_literals;
+            dumper.dump("(uid, frameRate)"sv, uid, frameRate);
+        }
     }
-    StringAppendF(&result, "}\n");
 }
 
 bool FrameRateOverrideMappings::updateFrameRateOverridesByContent(
diff --git a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
index 4185a4c..da0f276 100644
--- a/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
+++ b/services/surfaceflinger/Scheduler/FrameRateOverrideMappings.h
@@ -23,7 +23,10 @@
 #include <map>
 #include <optional>
 
+#include "Utils/Dumper.h"
+
 namespace android::scheduler {
+
 class FrameRateOverrideMappings {
     using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
     using UidToFrameRateOverride = std::map<uid_t, Fps>;
@@ -34,7 +37,6 @@
             EXCLUDES(mFrameRateOverridesLock);
     std::vector<FrameRateOverride> getAllFrameRateOverrides(bool supportsFrameRateOverrideByContent)
             EXCLUDES(mFrameRateOverridesLock);
-    void dump(std::string& result) const;
     bool updateFrameRateOverridesByContent(const UidToFrameRateOverride& frameRateOverrides)
             EXCLUDES(mFrameRateOverridesLock);
     void setGameModeRefreshRateForUid(FrameRateOverride frameRateOverride)
@@ -42,7 +44,17 @@
     void setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride)
             EXCLUDES(mFrameRateOverridesLock);
 
+    void dump(utils::Dumper&) const;
+
 private:
+    size_t maxOverridesCount() const REQUIRES(mFrameRateOverridesLock) {
+        return std::max({mFrameRateOverridesByContent.size(),
+                         mFrameRateOverridesFromGameManager.size(),
+                         mFrameRateOverridesFromBackdoor.size()});
+    }
+
+    void dump(utils::Dumper&, std::string_view name, const UidToFrameRateOverride&) const;
+
     // The frame rate override lists need their own mutex as they are being read
     // by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
     mutable std::mutex mFrameRateOverridesLock;
@@ -53,4 +65,5 @@
     UidToFrameRateOverride mFrameRateOverridesFromBackdoor GUARDED_BY(mFrameRateOverridesLock);
     UidToFrameRateOverride mFrameRateOverridesFromGameManager GUARDED_BY(mFrameRateOverridesLock);
 };
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
new file mode 100644
index 0000000..92c2189
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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 <ui/DisplayId.h>
+
+#include "Display/DisplayModeRequest.h"
+
+namespace android::scheduler {
+
+struct ISchedulerCallback {
+    virtual void setVsyncEnabled(PhysicalDisplayId, bool) = 0;
+    virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
+    virtual void kernelTimerChanged(bool expired) = 0;
+    virtual void triggerOnFrameRateOverridesChanged() = 0;
+
+protected:
+    ~ISchedulerCallback() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
deleted file mode 100644
index 760a4ee..0000000
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ /dev/null
@@ -1,57 +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 <mutex>
-
-#include "EventThread.h"
-
-namespace android {
-
-/**
- * VSync signals used during SurfaceFlinger trace playback (traces we captured
- * with SurfaceInterceptor).
- */
-class InjectVSyncSource final : public VSyncSource {
-public:
-    ~InjectVSyncSource() override = default;
-
-    void setCallback(VSyncSource::Callback* callback) override {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        mCallback = callback;
-    }
-
-    void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp,
-                           nsecs_t deadlineTimestamp) {
-        std::lock_guard<std::mutex> lock(mCallbackMutex);
-        if (mCallback) {
-            mCallback->onVSyncEvent(when, {expectedVSyncTimestamp, deadlineTimestamp});
-        }
-    }
-
-    const char* getName() const override { return "inject"; }
-    void setVSyncEnabled(bool) override {}
-    void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {}
-    VSyncData getLatestVSyncData() const override { return {}; }
-    void dump(std::string&) const override {}
-
-private:
-    std::mutex mCallbackMutex;
-    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5f64efa..beaf972 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -22,9 +22,9 @@
 
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
+#include <gui/TraceUtils.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
-#include <utils/Trace.h>
 
 #include <algorithm>
 #include <cmath>
@@ -32,6 +32,7 @@
 #include <utility>
 
 #include "../Layer.h"
+#include "EventThread.h"
 #include "LayerInfo.h"
 
 namespace android::scheduler {
@@ -72,6 +73,20 @@
 
     ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
 }
+
+LayerHistory::LayerVoteType getVoteType(LayerInfo::FrameRateCompatibility compatibility,
+                                        bool contentDetectionEnabled) {
+    LayerHistory::LayerVoteType voteType;
+    if (!contentDetectionEnabled || compatibility == LayerInfo::FrameRateCompatibility::NoVote) {
+        voteType = LayerHistory::LayerVoteType::NoVote;
+    } else if (compatibility == LayerInfo::FrameRateCompatibility::Min) {
+        voteType = LayerHistory::LayerVoteType::Min;
+    } else {
+        voteType = LayerHistory::LayerVoteType::Heuristic;
+    }
+    return voteType;
+}
+
 } // namespace
 
 LayerHistory::LayerHistory()
@@ -81,10 +96,12 @@
 
 LayerHistory::~LayerHistory() = default;
 
-void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) {
     std::lock_guard lock(mLock);
     LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound,
                         "%s already registered", layer->getName().c_str());
+    LayerVoteType type =
+            getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled);
     auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
 
     // The layer can be placed on either map, it is assumed that partitionLayers() will be called
@@ -101,8 +118,44 @@
     }
 }
 
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
-                          LayerUpdateType updateType) {
+void LayerHistory::record(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+                          nsecs_t now, LayerUpdateType updateType) {
+    std::lock_guard lock(mLock);
+    auto [found, layerPair] = findLayer(id);
+    if (found == LayerStatus::NotFound) {
+        // Offscreen layer
+        ALOGV("%s: %d not registered", __func__, id);
+        return;
+    }
+
+    const auto& info = layerPair->second;
+    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
+
+    // Set frame rate to attached choreographer.
+    // TODO(b/260898223): Change to use layer hierarchy and handle frame rate vote.
+    if (updateType == LayerUpdateType::SetFrameRate) {
+        auto range = mAttachedChoreographers.equal_range(id);
+        auto it = range.first;
+        while (it != range.second) {
+            sp<EventThreadConnection> choreographerConnection = it->second.promote();
+            if (choreographerConnection) {
+                choreographerConnection->frameRate = layerProps.setFrameRateVote.rate;
+                it++;
+            } else {
+                it = mAttachedChoreographers.erase(it);
+            }
+        }
+    }
+
+    // Activate layer if inactive.
+    if (found == LayerStatus::LayerInInactiveMap) {
+        mActiveLayerInfos.insert(
+                {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+        mInactiveLayerInfos.erase(id);
+    }
+}
+
+void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) {
     std::lock_guard lock(mLock);
     auto id = layer->getSequence();
 
@@ -114,25 +167,12 @@
     }
 
     const auto& info = layerPair->second;
-    const auto layerProps = LayerInfo::LayerProps{
-            .visible = layer->isVisible(),
-            .bounds = layer->getBounds(),
-            .transform = layer->getTransform(),
-            .setFrameRateVote = layer->getFrameRateForLayerTree(),
-            .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
-    };
-
-    info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
-
-    // Activate layer if inactive.
-    if (found == LayerStatus::LayerInInactiveMap) {
-        mActiveLayerInfos.insert(
-                {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
-        mInactiveLayerInfos.erase(id);
-    }
+    info->setDefaultLayerVote(
+            getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
 }
 
-auto LayerHistory::summarize(const RefreshRateConfigs& configs, nsecs_t now) -> Summary {
+auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
+    ATRACE_CALL();
     Summary summary;
 
     std::lock_guard lock(mLock);
@@ -146,7 +186,8 @@
         ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
               layerFocused ? "" : "not");
 
-        const auto vote = info->getRefreshRateVote(configs, now);
+        ATRACE_FORMAT("%s", info->getName().c_str());
+        const auto vote = info->getRefreshRateVote(selector, now);
         // Skip NoVote layer as those don't have any requirements
         if (vote.type == LayerVoteType::NoVote) {
             continue;
@@ -160,6 +201,8 @@
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+        ATRACE_FORMAT_INSTANT("%s %s (%d%)", ftl::enum_string(vote.type).c_str(),
+                              to_string(vote.fps).c_str(), weight * 100);
         summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
                            vote.seamlessness, weight, layerFocused});
 
@@ -172,6 +215,7 @@
 }
 
 void LayerHistory::partitionLayers(nsecs_t now) {
+    ATRACE_CALL();
     const nsecs_t threshold = getActiveLayerThreshold(now);
 
     // iterate over inactive map
@@ -203,6 +247,8 @@
                 switch (frameRate.type) {
                     case Layer::FrameRateCompatibility::Default:
                         return LayerVoteType::ExplicitDefault;
+                    case Layer::FrameRateCompatibility::Min:
+                        return LayerVoteType::Min;
                     case Layer::FrameRateCompatibility::ExactOrMultiple:
                         return LayerVoteType::ExplicitExactOrMultiple;
                     case Layer::FrameRateCompatibility::NoVote:
@@ -241,7 +287,7 @@
 
 std::string LayerHistory::dump() const {
     std::lock_guard lock(mLock);
-    return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+    return base::StringPrintf("{size=%zu, active=%zu}",
                               mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
                               mActiveLayerInfos.size());
 }
@@ -255,6 +301,12 @@
     return 0.f;
 }
 
+void LayerHistory::attachChoreographer(int32_t layerId,
+                                       const sp<EventThreadConnection>& choreographerConnection) {
+    std::lock_guard lock(mLock);
+    mAttachedChoreographers.insert({layerId, wp<EventThreadConnection>(choreographerConnection)});
+}
+
 auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> {
     // the layer could be in either the active or inactive map, try both
     auto it = mActiveLayerInfos.find(id);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 7b6096f..69caf9f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -27,7 +27,9 @@
 #include <utility>
 #include <vector>
 
-#include "RefreshRateConfigs.h"
+#include "EventThread.h"
+
+#include "RefreshRateSelector.h"
 
 namespace android {
 
@@ -36,16 +38,17 @@
 namespace scheduler {
 
 class LayerInfo;
+struct LayerProps;
 
 class LayerHistory {
 public:
-    using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+    using LayerVoteType = RefreshRateSelector::LayerVoteType;
 
     LayerHistory();
     ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, LayerVoteType type);
+    void registerLayer(Layer*, bool contentDetectionEnabled);
 
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
@@ -61,12 +64,17 @@
     };
 
     // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
+    void record(int32_t id, const LayerProps& props, nsecs_t presentTime, nsecs_t now,
+                LayerUpdateType updateType);
 
-    using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
+    // Updates the default frame rate compatibility which takes effect when the app
+    // does not set a preference for refresh rate.
+    void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
+
+    using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    Summary summarize(const RefreshRateConfigs&, nsecs_t now);
+    Summary summarize(const RefreshRateSelector&, nsecs_t now);
 
     void clear();
 
@@ -76,6 +84,9 @@
     // return the frames per second of the layer with the given sequence id.
     float getLayerFramerate(nsecs_t now, int32_t id) const;
 
+    void attachChoreographer(int32_t layerId,
+                             const sp<EventThreadConnection>& choreographerConnection);
+
 private:
     friend class LayerHistoryTest;
     friend class TestableScheduler;
@@ -113,6 +124,10 @@
     LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
     LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
 
+    // Map keyed by layer ID (sequence) to choreographer connections.
+    std::unordered_multimap<int32_t, wp<EventThreadConnection>> mAttachedChoreographers
+            GUARDED_BY(mLock);
+
     uint32_t mDisplayArea = 0;
 
     // Whether to emit systrace output and debug logs.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 943615c..bae3739 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -29,6 +29,7 @@
 #include <cutils/compiler.h>
 #include <cutils/trace.h>
 #include <ftl/enum.h>
+#include <gui/TraceUtils.h>
 
 #undef LOG_TAG
 #define LOG_TAG "LayerInfo"
@@ -43,14 +44,17 @@
         mOwnerUid(ownerUid),
         mDefaultVote(defaultVote),
         mLayerVote({defaultVote, Fps()}),
-        mRefreshRateHistory(name) {}
+        mLayerProps(std::make_unique<LayerProps>()),
+        mRefreshRateHistory(name) {
+    ;
+}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                                   bool pendingModeChange, LayerProps props) {
+                                   bool pendingModeChange, const LayerProps& props) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     mLastUpdatedTime = std::max(lastPresentTime, now);
-    mLayerProps = props;
+    *mLayerProps = props;
     switch (updateType) {
         case LayerUpdateType::AnimationTX:
             mLastAnimationTime = std::max(lastPresentTime, now);
@@ -74,14 +78,52 @@
                                           .count();
 }
 
-bool LayerInfo::isFrequent(nsecs_t now) const {
-    using fps_approx_ops::operator>=;
-    // If we know nothing about this layer we consider it as frequent as it might be the start
-    // of an animation.
+LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const {
+    // If we know nothing about this layer (e.g. after touch event),
+    // we consider it as frequent as it might be the start of an animation.
     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
-        return true;
+        return {/* isFrequent */ true, /* clearHistory */ false, /* isConclusive */ true};
     }
-    return getFps(now) >= kMinFpsForFrequentLayer;
+
+    // Non-active layers are also infrequent
+    if (mLastUpdatedTime < getActiveLayerThreshold(now)) {
+        return {/* isFrequent */ false, /* clearHistory */ false, /* isConclusive */ true};
+    }
+
+    // We check whether we can classify this layer as frequent or infrequent:
+    //  - frequent: a layer posted kFrequentLayerWindowSize within
+    //              kMaxPeriodForFrequentLayerNs of each other.
+    // -  infrequent: a layer posted kFrequentLayerWindowSize with longer
+    //                gaps than kFrequentLayerWindowSize.
+    // If we can't determine the layer classification yet, we return the last
+    // classification.
+    bool isFrequent = true;
+    bool isInfrequent = true;
+    const auto n = mFrameTimes.size() - 1;
+    for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) {
+        if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime <
+            kMaxPeriodForFrequentLayerNs.count()) {
+            isInfrequent = false;
+        } else {
+            isFrequent = false;
+        }
+    }
+
+    if (isFrequent || isInfrequent) {
+        // If the layer was previously inconclusive, we clear
+        // the history as indeterminate layers changed to frequent,
+        // and we should not look at the stale data.
+        return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true};
+    }
+
+    // If we can't determine whether the layer is frequent or not, we return
+    // the last known classification and mark the layer frequency as inconclusive.
+    isFrequent = !mLastRefreshRate.infrequent;
+
+    // If the layer was previously tagged as animating, we clear
+    // the history as it is likely the layer just changed its behavior,
+    // and we should not look at stale data.
+    return {isFrequent, isFrequent && mLastRefreshRate.animating, /* isConclusive */ false};
 }
 
 Fps LayerInfo::getFps(nsecs_t now) const {
@@ -187,8 +229,9 @@
     return static_cast<nsecs_t>(averageFrameTime);
 }
 
-std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(
-        const RefreshRateConfigs& refreshRateConfigs, nsecs_t now) {
+std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSelector& selector,
+                                                             nsecs_t now) {
+    ATRACE_CALL();
     static constexpr float MARGIN = 1.0f; // 1Hz
     if (!hasEnoughDataForHeuristic()) {
         ALOGV("Not enough data");
@@ -199,7 +242,7 @@
         const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
         const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
         if (refreshRateConsistent) {
-            const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
+            const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate);
             using fps_approx_ops::operator!=;
 
             // To avoid oscillation, use the last calculated refresh rate if it is close enough.
@@ -222,35 +265,37 @@
                                                : std::nullopt;
 }
 
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateConfigs& refreshRateConfigs,
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
                                                    nsecs_t now) {
+    ATRACE_CALL();
     if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
         ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
         return mLayerVote;
     }
 
     if (isAnimating(now)) {
+        ATRACE_FORMAT_INSTANT("animating");
         ALOGV("%s is animating", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
+        mLastRefreshRate.animating = true;
         return {LayerHistory::LayerVoteType::Max, Fps()};
     }
 
-    if (!isFrequent(now)) {
+    const LayerInfo::Frequent frequent = isFrequent(now);
+    mIsFrequencyConclusive = frequent.isConclusive;
+    if (!frequent.isFrequent) {
+        ATRACE_FORMAT_INSTANT("infrequent");
         ALOGV("%s is infrequent", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
-        // Infrequent layers vote for mininal refresh rate for
+        mLastRefreshRate.infrequent = true;
+        // Infrequent layers vote for minimal refresh rate for
         // battery saving purposes and also to prevent b/135718869.
         return {LayerHistory::LayerVoteType::Min, Fps()};
     }
 
-    // If the layer was previously tagged as animating or infrequent, we clear
-    // the history as it is likely the layer just changed its behavior
-    // and we should not look at stale data
-    if (mLastRefreshRate.animatingOrInfrequent) {
+    if (frequent.clearHistory) {
         clearHistory(now);
     }
 
-    auto refreshRate = calculateRefreshRateIfPossible(refreshRateConfigs, now);
+    auto refreshRate = calculateRefreshRateIfPossible(selector, now);
     if (refreshRate.has_value()) {
         ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
         return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
@@ -269,6 +314,26 @@
     return mTraceTags.at(type).c_str();
 }
 
+LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const {
+    return mLayerProps->setFrameRateVote;
+}
+
+bool LayerInfo::isVisible() const {
+    return mLayerProps->visible;
+}
+
+int32_t LayerInfo::getFrameRateSelectionPriority() const {
+    return mLayerProps->frameRateSelectionPriority;
+}
+
+FloatRect LayerInfo::getBounds() const {
+    return mLayerProps->bounds;
+}
+
+ui::Transform LayerInfo::getTransform() const {
+    return mLayerProps->transform;
+}
+
 LayerInfo::RefreshRateHistory::HeuristicTraceTagData
 LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
     const std::string prefix = "LFPS ";
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 8a3b0b9..c5a6057 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -28,7 +28,7 @@
 #include <scheduler/Seamlessness.h>
 
 #include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 
 namespace android {
 
@@ -37,7 +37,7 @@
 namespace scheduler {
 
 using namespace std::chrono_literals;
-
+struct LayerProps;
 // Maximum period between presents for a layer to be considered active.
 constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
 
@@ -53,7 +53,7 @@
     // Layer is considered frequent if the earliest value in the window of most recent present times
     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
     // favor of a low refresh rate.
-    static constexpr size_t kFrequentLayerWindowSize = 3;
+    static constexpr size_t kFrequentLayerWindowSize = 4;
     static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
     static constexpr auto kMaxPeriodForFrequentLayerNs =
             std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
@@ -74,6 +74,8 @@
     enum class FrameRateCompatibility {
         Default, // Layer didn't specify any specific handling strategy
 
+        Min, // Layer needs the minimum frame rate.
+
         Exact, // Layer needs the exact frame rate.
 
         ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
@@ -130,19 +132,11 @@
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
 
-    struct LayerProps {
-        bool visible = false;
-        FloatRect bounds;
-        ui::Transform transform;
-        FrameRate setFrameRateVote;
-        int32_t frameRateSelectionPriority = -1;
-    };
-
     // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                            bool pendingModeChange, LayerProps props);
+                            bool pendingModeChange, const LayerProps& props);
 
     // Sets an explicit layer vote. This usually comes directly from the application via
     // ANativeWindow_setFrameRate API
@@ -160,19 +154,17 @@
 
     uid_t getOwnerUid() const { return mOwnerUid; }
 
-    LayerVote getRefreshRateVote(const RefreshRateConfigs&, nsecs_t now);
+    LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
-    FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; }
-    bool isVisible() const { return mLayerProps.visible; }
-    int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; }
-
-    FloatRect getBounds() const { return mLayerProps.bounds; }
-
-    ui::Transform getTransform() const { return mLayerProps.transform; }
+    FrameRate getSetFrameRateVote() const;
+    bool isVisible() const;
+    int32_t getFrameRateSelectionPriority() const;
+    FloatRect getBounds() const;
+    ui::Transform getTransform() const;
 
     // Returns a C string for tracing a vote
     const char* getTraceTag(LayerHistory::LayerVoteType type) const;
@@ -189,6 +181,7 @@
         mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
         mLastRefreshRate = {};
         mRefreshRateHistory.clear();
+        mIsFrequencyConclusive = true;
     }
 
     void clearHistory(nsecs_t now) {
@@ -212,7 +205,10 @@
         Fps reported;
         // Whether the last reported rate for LayerInfo::getRefreshRate()
         // was due to animation or infrequent updates
-        bool animatingOrInfrequent = false;
+        bool animating = false;
+        // Whether the last reported rate for LayerInfo::getRefreshRate()
+        // was due to infrequent updates
+        bool infrequent = false;
     };
 
     // Class to store past calculated refresh rate and determine whether
@@ -256,10 +252,18 @@
         static constexpr float MARGIN_CONSISTENT_FPS = 1.0;
     };
 
-    bool isFrequent(nsecs_t now) const;
+    // Represents whether we were able to determine either layer is frequent or infrequent
+    bool mIsFrequencyConclusive = true;
+    struct Frequent {
+        bool isFrequent;
+        bool clearHistory;
+        // Represents whether we were able to determine isFrequent conclusively
+        bool isConclusive;
+    };
+    Frequent isFrequent(nsecs_t now) const;
     bool isAnimating(nsecs_t now) const;
     bool hasEnoughDataForHeuristic() const;
-    std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateConfigs&, nsecs_t now);
+    std::optional<Fps> calculateRefreshRateIfPossible(const RefreshRateSelector&, nsecs_t now);
     std::optional<nsecs_t> calculateAverageFrameTime() const;
     bool isFrameTimeValid(const FrameTimeData&) const;
 
@@ -289,7 +293,7 @@
     static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
     static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
 
-    LayerProps mLayerProps;
+    std::unique_ptr<LayerProps> mLayerProps;
 
     RefreshRateHistory mRefreshRateHistory;
 
@@ -299,5 +303,13 @@
     static bool sTraceEnabled;
 };
 
+struct LayerProps {
+    bool visible = false;
+    FloatRect bounds;
+    ui::Transform transform;
+    LayerInfo::FrameRate setFrameRateVote;
+    int32_t frameRateSelectionPriority = -1;
+};
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index f2af85e..18c0a69 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -17,12 +17,12 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <binder/IPCThreadState.h>
-
+#include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
 
-#include <gui/DisplayEventReceiver.h>
+#include <scheduler/interface/ICompositor.h>
 
 #include "EventThread.h"
 #include "FrameTimeline.h"
@@ -30,11 +30,11 @@
 
 namespace android::impl {
 
-void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) {
+void MessageQueue::Handler::dispatchFrame(VsyncId vsyncId, TimePoint expectedVsyncTime) {
     if (!mFramePending.exchange(true)) {
         mVsyncId = vsyncId;
         mExpectedVsyncTime = expectedVsyncTime;
-        mQueue.mLooper->sendMessage(this, Message());
+        mQueue.mLooper->sendMessage(sp<MessageHandler>::fromExisting(this), Message());
     }
 }
 
@@ -44,16 +44,7 @@
 
 void MessageQueue::Handler::handleMessage(const Message&) {
     mFramePending.store(false);
-
-    const nsecs_t frameTime = systemTime();
-    auto& compositor = mQueue.mCompositor;
-
-    if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
-        return;
-    }
-
-    compositor.composite(frameTime, mVsyncId);
-    compositor.sample();
+    mQueue.onFrameSignal(mQueue.mCompositor, mVsyncId, mExpectedVsyncTime);
 }
 
 MessageQueue::MessageQueue(ICompositor& compositor)
@@ -66,77 +57,93 @@
         mLooper(sp<Looper>::make(kAllowNonCallbacks)),
         mHandler(std::move(handler)) {}
 
-// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
-// and remove the EventThread from MessageQueue
-void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
-    auto& tube = mInjector.tube;
-
-    if (const int fd = tube.getFd(); fd >= 0) {
-        mLooper->removeFd(fd);
-    }
-
-    if (connection) {
-        // The EventThreadConnection is retained when disabling injection, so avoid subsequently
-        // stealing invalid FDs. Note that the stolen FDs are kept open.
-        if (tube.getFd() < 0) {
-            connection->stealReceiveChannel(&tube);
-        } else {
-            ALOGW("Recycling channel for VSYNC injection.");
-        }
-
-        mLooper->addFd(
-                tube.getFd(), 0, Looper::EVENT_INPUT,
-                [](int, int, void* data) {
-                    reinterpret_cast<MessageQueue*>(data)->injectorCallback();
-                    return 1; // Keep registration.
-                },
-                this);
-    }
-
-    std::lock_guard lock(mInjector.mutex);
-    mInjector.connection = std::move(connection);
-}
-
 void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
     ATRACE_CALL();
     // Trace VSYNC-sf
     mVsync.value = (mVsync.value + 1) % 2;
 
+    const auto expectedVsyncTime = TimePoint::fromNs(vsyncTime);
     {
         std::lock_guard lock(mVsync.mutex);
-        mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+        mVsync.lastCallbackTime = expectedVsyncTime;
         mVsync.scheduledFrameTime.reset();
     }
 
-    const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
-            {targetWakeupTime, readyTime, vsyncTime});
+    const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions(
+            {targetWakeupTime, readyTime, vsyncTime})};
 
-    mHandler->dispatchFrame(vsyncId, vsyncTime);
+    mHandler->dispatchFrame(vsyncId, expectedVsyncTime);
 }
 
-void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
+void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch,
                              frametimeline::TokenManager& tokenManager,
                              std::chrono::nanoseconds workDuration) {
-    setDuration(workDuration);
-    mVsync.tokenManager = &tokenManager;
+    std::unique_ptr<scheduler::VSyncCallbackRegistration> oldRegistration;
+    {
+        std::lock_guard lock(mVsync.mutex);
+        mVsync.workDuration = workDuration;
+        mVsync.tokenManager = &tokenManager;
+        oldRegistration = onNewVsyncScheduleLocked(std::move(dispatch));
+    }
+
+    // See comments in onNewVsyncSchedule. Today, oldRegistration should be
+    // empty, but nothing prevents us from calling initVsync multiple times, so
+    // go ahead and destruct it outside the lock for safety.
+    oldRegistration.reset();
+}
+
+void MessageQueue::onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch> dispatch) {
+    std::unique_ptr<scheduler::VSyncCallbackRegistration> oldRegistration;
+    {
+        std::lock_guard lock(mVsync.mutex);
+        oldRegistration = onNewVsyncScheduleLocked(std::move(dispatch));
+    }
+
+    // The old registration needs to be deleted after releasing mVsync.mutex to
+    // avoid deadlock. This is because the callback may be running on the timer
+    // thread. In that case, timerCallback sets
+    // VSyncDispatchTimerQueueEntry::mRunning to true, then attempts to lock
+    // mVsync.mutex. But if it's already locked, the VSyncCallbackRegistration's
+    // destructor has to wait until VSyncDispatchTimerQueueEntry::mRunning is
+    // set back to false, but it won't be until mVsync.mutex is released.
+    oldRegistration.reset();
+}
+
+std::unique_ptr<scheduler::VSyncCallbackRegistration> MessageQueue::onNewVsyncScheduleLocked(
+        std::shared_ptr<scheduler::VSyncDispatch> dispatch) {
+    const bool reschedule = mVsync.registration &&
+            mVsync.registration->cancel() == scheduler::CancelResult::Cancelled;
+    auto oldRegistration = std::move(mVsync.registration);
     mVsync.registration = std::make_unique<
-            scheduler::VSyncCallbackRegistration>(dispatch,
+            scheduler::VSyncCallbackRegistration>(std::move(dispatch),
                                                   std::bind(&MessageQueue::vsyncCallback, this,
                                                             std::placeholders::_1,
                                                             std::placeholders::_2,
                                                             std::placeholders::_3),
                                                   "sf");
+    if (reschedule) {
+        mVsync.scheduledFrameTime =
+                mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+                                               .readyDuration = 0,
+                                               .earliestVsync = mVsync.lastCallbackTime.ns()});
+    }
+    return oldRegistration;
+}
+
+void MessageQueue::destroyVsync() {
+    std::lock_guard lock(mVsync.mutex);
+    mVsync.tokenManager = nullptr;
+    mVsync.registration.reset();
 }
 
 void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) {
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    if (mVsync.scheduledFrameTime) {
-        mVsync.scheduledFrameTime = mVsync.registration->schedule(
-                {mVsync.workDuration.get().count(),
-                 /*readyDuration=*/0, mVsync.lastCallbackTime.count()});
-    }
+    mVsync.scheduledFrameTime =
+            mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
+                                         .readyDuration = 0,
+                                         .earliestVsync = mVsync.lastCallbackTime.ns()});
 }
 
 void MessageQueue::waitMessage() {
@@ -165,38 +172,31 @@
     mLooper->sendMessage(handler, Message());
 }
 
+void MessageQueue::postMessageDelayed(sp<MessageHandler>&& handler, nsecs_t uptimeDelay) {
+    mLooper->sendMessageDelayed(uptimeDelay, handler, Message());
+}
+
+void MessageQueue::scheduleConfigure() {
+    struct ConfigureHandler : MessageHandler {
+        explicit ConfigureHandler(ICompositor& compositor) : compositor(compositor) {}
+
+        void handleMessage(const Message&) override { compositor.configure(); }
+
+        ICompositor& compositor;
+    };
+
+    // TODO(b/241285876): Batch configure tasks that happen within some duration.
+    postMessage(sp<ConfigureHandler>::make(mCompositor));
+}
+
 void MessageQueue::scheduleFrame() {
     ATRACE_CALL();
 
-    {
-        std::lock_guard lock(mInjector.mutex);
-        if (CC_UNLIKELY(mInjector.connection)) {
-            ALOGD("%s while injecting VSYNC", __FUNCTION__);
-            mInjector.connection->requestNextVsync();
-            return;
-        }
-    }
-
     std::lock_guard lock(mVsync.mutex);
     mVsync.scheduledFrameTime =
             mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                            .readyDuration = 0,
-                                           .earliestVsync = mVsync.lastCallbackTime.count()});
-}
-
-void MessageQueue::injectorCallback() {
-    ssize_t n;
-    DisplayEventReceiver::Event buffer[8];
-    while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
-        for (int i = 0; i < n; i++) {
-            if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                auto& vsync = buffer[i].vsync;
-                mHandler->dispatchFrame(vsync.vsyncData.preferredVsyncId(),
-                                        vsync.vsyncData.preferredExpectedPresentationTime());
-                break;
-            }
-        }
-    }
+                                           .earliestVsync = mVsync.lastCallbackTime.ns()});
 }
 
 auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 4082e26..a523147 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -25,28 +25,28 @@
 #include <android/gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
 #include <utils/Looper.h>
+#include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
+#include <scheduler/Time.h>
+#include <scheduler/VsyncId.h>
+
 #include "EventThread.h"
 #include "TracedOrdinal.h"
 #include "VSyncDispatch.h"
 
 namespace android {
 
-struct ICompositor {
-    virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
-    virtual void composite(nsecs_t frameTime, int64_t vsyncId) = 0;
-    virtual void sample() = 0;
-
-protected:
-    ~ICompositor() = default;
-};
+struct ICompositor;
 
 template <typename F>
 class Task : public MessageHandler {
     template <typename G>
     friend auto makeTask(G&&);
 
+    template <typename... Args>
+    friend sp<Task<F>> sp<Task<F>>::make(Args&&... args);
+
     explicit Task(F&& f) : mTask(std::move(f)) {}
 
     void handleMessage(const Message&) override { mTask(); }
@@ -57,7 +57,7 @@
 
 template <typename F>
 inline auto makeTask(F&& f) {
-    sp<Task<F>> task = new Task<F>(std::move(f));
+    sp<Task<F>> task = sp<Task<F>>::make(std::forward<F>(f));
     return std::make_pair(task, task->mTask.get_future());
 }
 
@@ -65,12 +65,14 @@
 public:
     virtual ~MessageQueue() = default;
 
-    virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+    virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
                            std::chrono::nanoseconds workDuration) = 0;
+    virtual void destroyVsync() = 0;
     virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
-    virtual void setInjector(sp<EventThreadConnection>) = 0;
     virtual void waitMessage() = 0;
     virtual void postMessage(sp<MessageHandler>&&) = 0;
+    virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
+    virtual void scheduleConfigure() = 0;
     virtual void scheduleFrame() = 0;
 
     using Clock = std::chrono::steady_clock;
@@ -84,8 +86,9 @@
     class Handler : public MessageHandler {
         MessageQueue& mQueue;
         std::atomic_bool mFramePending = false;
-        std::atomic<int64_t> mVsyncId = 0;
-        std::atomic<nsecs_t> mExpectedVsyncTime = 0;
+
+        std::atomic<VsyncId> mVsyncId;
+        std::atomic<TimePoint> mExpectedVsyncTime;
 
     public:
         explicit Handler(MessageQueue& queue) : mQueue(queue) {}
@@ -93,7 +96,7 @@
 
         bool isFramePending() const;
 
-        virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime);
+        virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime);
     };
 
     friend class Handler;
@@ -103,45 +106,47 @@
 
     void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
 
+    void onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch>) EXCLUDES(mVsync.mutex);
+
 private:
+    virtual void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) = 0;
+
     ICompositor& mCompositor;
     const sp<Looper> mLooper;
     const sp<Handler> mHandler;
 
     struct Vsync {
         frametimeline::TokenManager* tokenManager = nullptr;
-        std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
 
         mutable std::mutex mutex;
+        std::unique_ptr<scheduler::VSyncCallbackRegistration> registration GUARDED_BY(mutex);
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
-        std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+        TimePoint lastCallbackTime GUARDED_BY(mutex);
         std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
-    struct Injector {
-        gui::BitTube tube;
-        std::mutex mutex;
-        sp<EventThreadConnection> connection GUARDED_BY(mutex);
-    };
-
     Vsync mVsync;
-    Injector mInjector;
 
-    void injectorCallback();
+    // Returns the old registration so it can be destructed outside the lock to
+    // avoid deadlock.
+    std::unique_ptr<scheduler::VSyncCallbackRegistration> onNewVsyncScheduleLocked(
+            std::shared_ptr<scheduler::VSyncDispatch>) REQUIRES(mVsync.mutex);
 
 public:
     explicit MessageQueue(ICompositor&);
 
-    void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
+    void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&,
                    std::chrono::nanoseconds workDuration) override;
+    void destroyVsync() override;
     void setDuration(std::chrono::nanoseconds workDuration) override;
-    void setInjector(sp<EventThreadConnection>) override;
 
     void waitMessage() override;
     void postMessage(sp<MessageHandler>&&) override;
+    void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
 
+    void scheduleConfigure() override;
     void scheduleFrame() override;
 
     std::optional<Clock::time_point> getScheduledFrameTime() const override;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 3c8dc64..cd45bfd 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -179,11 +179,5 @@
     }
 }
 
-std::string OneShotTimer::dump() const {
-    std::ostringstream stream;
-    stream << mInterval.count() << " ms";
-    return stream.str();
-}
-
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 2017c31..02e8719 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -23,6 +23,7 @@
 #include "../Clock.h"
 
 #include <android-base/thread_annotations.h>
+#include <scheduler/Time.h>
 
 namespace android {
 namespace scheduler {
@@ -39,9 +40,11 @@
 
     OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback,
                  const TimeoutCallback& timeoutCallback,
-                 std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>());
+                 std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>());
     ~OneShotTimer();
 
+    Duration interval() const { return mInterval; }
+
     // Initializes and turns on the idle timer.
     void start();
     // Stops the idle timer and any held resources.
@@ -49,8 +52,6 @@
     // Resets the wakeup time and fires the reset callback.
     void reset();
 
-    std::string dump() const;
-
 private:
     // Enum to track in what state is the timer.
     enum class TimerState {
@@ -81,7 +82,7 @@
     std::thread mThread;
 
     // Clock object for the timer. Mocked in unit tests.
-    std::unique_ptr<Clock> mClock;
+    std::unique_ptr<android::Clock> mClock;
 
     // Semaphore to keep mThread synchronized.
     sem_t mSemaphore;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
deleted file mode 100644
index a48c921..0000000
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ /dev/null
@@ -1,1024 +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.
- */
-
-// #define LOG_NDEBUG 0
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <chrono>
-#include <cmath>
-
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <ftl/enum.h>
-#include <utils/Trace.h>
-
-#include "../SurfaceFlingerProperties.h"
-#include "RefreshRateConfigs.h"
-
-#undef LOG_TAG
-#define LOG_TAG "RefreshRateConfigs"
-
-namespace android::scheduler {
-namespace {
-
-struct RefreshRateScore {
-    DisplayModeIterator modeIt;
-    float overallScore;
-    struct {
-        float modeBelowThreshold;
-        float modeAboveThreshold;
-    } fixedRateBelowThresholdLayersScore;
-};
-
-template <typename Iterator>
-const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
-    const auto it =
-            std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
-                const auto& [modeIt, overallScore, _] = current;
-
-                std::string name = to_string(modeIt->second->getFps());
-                ALOGV("%s scores %.2f", name.c_str(), overallScore);
-
-                ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100)));
-
-                constexpr float kEpsilon = 0.0001f;
-                return overallScore > max.overallScore * (1 + kEpsilon);
-            });
-
-    return it->modeIt->second;
-}
-
-constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
-
-std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
-    return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
-                              ftl::enum_string(layer.vote).c_str(), weight,
-                              ftl::enum_string(layer.seamlessness).c_str(),
-                              to_string(layer.desiredRefreshRate).c_str());
-}
-
-std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
-    std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
-    knownFrameRates.reserve(knownFrameRates.size() + modes.size());
-
-    // Add all supported refresh rates.
-    for (const auto& [id, mode] : modes) {
-        knownFrameRates.push_back(mode->getFps());
-    }
-
-    // Sort and remove duplicates.
-    std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
-    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
-                                      isApproxEqual),
-                          knownFrameRates.end());
-    return knownFrameRates;
-}
-
-// The Filter is a `bool(const DisplayMode&)` predicate.
-template <typename Filter>
-std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
-    std::vector<DisplayModeIterator> sortedModes;
-    sortedModes.reserve(modes.size());
-
-    for (auto it = modes.begin(); it != modes.end(); ++it) {
-        const auto& [id, mode] = *it;
-
-        if (filter(*mode)) {
-            ALOGV("%s: including mode %d", __func__, id.value());
-            sortedModes.push_back(it);
-        }
-    }
-
-    std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
-        const auto& mode1 = it1->second;
-        const auto& mode2 = it2->second;
-
-        if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
-            return mode1->getGroup() > mode2->getGroup();
-        }
-
-        return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
-    });
-
-    return sortedModes;
-}
-
-bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
-    for (const auto it1 : sortedModes) {
-        const auto& mode1 = it1->second;
-        for (const auto it2 : sortedModes) {
-            const auto& mode2 = it2->second;
-
-            if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
-} // namespace
-
-std::string RefreshRateConfigs::Policy::toString() const {
-    return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
-                              ", primaryRange=%s, appRequestRange=%s}",
-                              defaultMode.value(), allowGroupSwitching ? "true" : "false",
-                              to_string(primaryRange).c_str(), to_string(appRequestRange).c_str());
-}
-
-std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
-                                                                 nsecs_t displayPeriod) const {
-    auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
-    if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
-        std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
-        quotient++;
-        remainder = 0;
-    }
-
-    return {quotient, remainder};
-}
-
-float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
-                                                                    Fps refreshRate) const {
-    constexpr float kScoreForFractionalPairs = .8f;
-
-    const auto displayPeriod = refreshRate.getPeriodNsecs();
-    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
-    if (layer.vote == LayerVoteType::ExplicitDefault) {
-        // Find the actual rate the layer will render, assuming
-        // that layerPeriod is the minimal period to render a frame.
-        // For example if layerPeriod is 20ms and displayPeriod is 16ms,
-        // then the actualLayerPeriod will be 32ms, because it is the
-        // smallest multiple of the display period which is >= layerPeriod.
-        auto actualLayerPeriod = displayPeriod;
-        int multiplier = 1;
-        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
-            multiplier++;
-            actualLayerPeriod = displayPeriod * multiplier;
-        }
-
-        // Because of the threshold we used above it's possible that score is slightly
-        // above 1.
-        return std::min(1.0f,
-                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
-    }
-
-    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
-        layer.vote == LayerVoteType::Heuristic) {
-        if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
-            return kScoreForFractionalPairs;
-        }
-
-        // Calculate how many display vsyncs we need to present a single frame for this
-        // layer
-        const auto [displayFramesQuotient, displayFramesRemainder] =
-                getDisplayFrames(layerPeriod, displayPeriod);
-        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
-        if (displayFramesRemainder == 0) {
-            // Layer desired refresh rate matches the display rate.
-            return 1.0f;
-        }
-
-        if (displayFramesQuotient == 0) {
-            // Layer desired refresh rate is higher than the display rate.
-            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
-                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
-        }
-
-        // Layer desired refresh rate is lower than the display rate. Check how well it fits
-        // the cadence.
-        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
-        int iter = 2;
-        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
-            diff = diff - (displayPeriod - diff);
-            iter++;
-        }
-
-        return (1.0f / iter);
-    }
-
-    return 0;
-}
-
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
-                                                    bool isSeamlessSwitch) const {
-    // Slightly prefer seamless switches.
-    constexpr float kSeamedSwitchPenalty = 0.95f;
-    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-
-    // If the layer wants Max, give higher score to the higher refresh rate
-    if (layer.vote == LayerVoteType::Max) {
-        const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second;
-        const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue();
-        // use ratio^2 to get a lower score the more we get further from peak
-        return ratio * ratio;
-    }
-
-    if (layer.vote == LayerVoteType::ExplicitExact) {
-        const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
-        if (mSupportsFrameRateOverrideByContent) {
-            // Since we support frame rate override, allow refresh rates which are
-            // multiples of the layer's request, as those apps would be throttled
-            // down to run at the desired refresh rate.
-            return divisor > 0;
-        }
-
-        return divisor == 1;
-    }
-
-    // If the layer frame rate is a divisor of the refresh rate it should score
-    // the highest score.
-    if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
-        return 1.0f * seamlessness;
-    }
-
-    // The layer frame rate is not a divisor of the refresh rate,
-    // there is a small penalty attached to the score to favor the frame rates
-    // the exactly matches the display refresh rate or a multiple.
-    constexpr float kNonExactMatchingPenalty = 0.95f;
-    return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
-            kNonExactMatchingPenalty;
-}
-
-auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
-                                            GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
-    std::lock_guard lock(mLock);
-
-    if (mGetBestRefreshRateCache &&
-        mGetBestRefreshRateCache->arguments == std::make_pair(layers, signals)) {
-        return mGetBestRefreshRateCache->result;
-    }
-
-    const auto result = getBestRefreshRateLocked(layers, signals);
-    mGetBestRefreshRateCache = GetBestRefreshRateCache{{layers, signals}, result};
-    return result;
-}
-
-auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
-                                                  GlobalSignals signals) const
-        -> std::pair<DisplayModePtr, GlobalSignals> {
-    using namespace fps_approx_ops;
-    ATRACE_CALL();
-    ALOGV("%s: %zu layers", __func__, layers.size());
-
-    int noVoteLayers = 0;
-    int minVoteLayers = 0;
-    int maxVoteLayers = 0;
-    int explicitDefaultVoteLayers = 0;
-    int explicitExactOrMultipleVoteLayers = 0;
-    int explicitExact = 0;
-    float maxExplicitWeight = 0;
-    int seamedFocusedLayers = 0;
-
-    for (const auto& layer : layers) {
-        switch (layer.vote) {
-            case LayerVoteType::NoVote:
-                noVoteLayers++;
-                break;
-            case LayerVoteType::Min:
-                minVoteLayers++;
-                break;
-            case LayerVoteType::Max:
-                maxVoteLayers++;
-                break;
-            case LayerVoteType::ExplicitDefault:
-                explicitDefaultVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
-                break;
-            case LayerVoteType::ExplicitExactOrMultiple:
-                explicitExactOrMultipleVoteLayers++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
-                break;
-            case LayerVoteType::ExplicitExact:
-                explicitExact++;
-                maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
-                break;
-            case LayerVoteType::Heuristic:
-                break;
-        }
-
-        if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
-            seamedFocusedLayers++;
-        }
-    }
-
-    const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
-            explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
-
-    const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
-    // If the default mode group is different from the group of current mode,
-    // this means a layer requesting a seamed mode switch just disappeared and
-    // we should switch back to the default group.
-    // However if a seamed layer is still present we anchor around the group
-    // of the current mode, in order to prevent unnecessary seamed mode switches
-    // (e.g. when pausing a video playback).
-    const auto anchorGroup =
-            seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup();
-
-    // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
-    // selected a refresh rate to see if we should apply touch boost.
-    if (signals.touch && !hasExplicitVoteLayers) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str());
-        return {max, GlobalSignals{.touch = true}};
-    }
-
-    // If the primary range consists of a single refresh rate then we can only
-    // move out the of range if layers explicitly request a different refresh
-    // rate.
-    const bool primaryRangeIsSingleRate =
-            isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
-
-    if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("Idle - choose %s", to_string(min->getFps()).c_str());
-        return {min, GlobalSignals{.idle = true}};
-    }
-
-    if (layers.empty() || noVoteLayers == layers.size()) {
-        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str());
-        return {max, kNoSignals};
-    }
-
-    // Only if all layers want Min we should return Min
-    if (noVoteLayers + minVoteLayers == layers.size()) {
-        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
-        ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str());
-        return {min, kNoSignals};
-    }
-
-    // Find the best refresh rate based on score
-    std::vector<RefreshRateScore> scores;
-    scores.reserve(mAppRequestRefreshRates.size());
-
-    for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
-        scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
-    }
-
-    for (const auto& layer : layers) {
-        ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
-              ftl::enum_string(layer.vote).c_str(), layer.weight,
-              layer.desiredRefreshRate.getValue());
-        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
-            continue;
-        }
-
-        const auto weight = layer.weight;
-
-        for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
-            const auto& [id, mode] = *modeIt;
-            const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
-
-            if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
-                ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
-                continue;
-            }
-
-            if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
-                !layer.focused) {
-                ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
-                      " Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
-                      to_string(*mActiveModeIt->second).c_str());
-                continue;
-            }
-
-            // Layers with default seamlessness vote for the current mode group if
-            // there are layers with seamlessness=SeamedAndSeamless and for the default
-            // mode group otherwise. In second case, if the current mode group is different
-            // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
-            // disappeared.
-            const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
-            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
-                ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str());
-                continue;
-            }
-
-            const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps());
-            if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
-                !(layer.focused &&
-                  (layer.vote == LayerVoteType::ExplicitDefault ||
-                   layer.vote == LayerVoteType::ExplicitExact))) {
-                // Only focused layers with ExplicitDefault frame rate settings are allowed to score
-                // refresh rates outside the primary range.
-                continue;
-            }
-
-            const float layerScore =
-                    calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
-            const float weightedLayerScore = weight * layerScore;
-
-            // Layer with fixed source has a special consideration which depends on the
-            // mConfig.frameRateMultipleThreshold. We don't want these layers to score
-            // refresh rates above the threshold, but we also don't want to favor the lower
-            // ones by having a greater number of layers scoring them. Instead, we calculate
-            // the score independently for these layers and later decide which
-            // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
-            // score 120 Hz, but desired 60 fps should contribute to the score.
-            const bool fixedSourceLayer = [](LayerVoteType vote) {
-                switch (vote) {
-                    case LayerVoteType::ExplicitExactOrMultiple:
-                    case LayerVoteType::Heuristic:
-                        return true;
-                    case LayerVoteType::NoVote:
-                    case LayerVoteType::Min:
-                    case LayerVoteType::Max:
-                    case LayerVoteType::ExplicitDefault:
-                    case LayerVoteType::ExplicitExact:
-                        return false;
-                }
-            }(layer.vote);
-            const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
-                    layer.desiredRefreshRate <
-                            Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
-            if (fixedSourceLayer && layerBelowThreshold) {
-                const bool modeAboveThreshold =
-                        mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
-                if (modeAboveThreshold) {
-                    ALOGV("%s gives %s fixed source (above threshold) score of %.4f",
-                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
-                          layerScore);
-                    fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
-                } else {
-                    ALOGV("%s gives %s fixed source (below threshold) score of %.4f",
-                          formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(),
-                          layerScore);
-                    fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
-                }
-            } else {
-                ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                      to_string(mode->getFps()).c_str(), layerScore);
-                overallScore += weightedLayerScore;
-            }
-        }
-    }
-
-    // We want to find the best refresh rate without the fixed source layers,
-    // so we could know whether we should add the modeAboveThreshold scores or not.
-    // If the best refresh rate is already above the threshold, it means that
-    // some non-fixed source layers already scored it, so we can just add the score
-    // for all fixed source layers, even the ones that are above the threshold.
-    const bool maxScoreAboveThreshold = [&] {
-        if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
-            return false;
-        }
-
-        const auto maxScoreIt =
-                std::max_element(scores.begin(), scores.end(),
-                                 [](RefreshRateScore max, RefreshRateScore current) {
-                                     const auto& [modeIt, overallScore, _] = current;
-                                     return overallScore > max.overallScore;
-                                 });
-        ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for "
-              "refresh rate multiples",
-              to_string(maxScoreIt->modeIt->second->getFps()).c_str(),
-              maxScoreAboveThreshold ? "above" : "below");
-        return maxScoreIt->modeIt->second->getFps() >=
-                Fps::fromValue(mConfig.frameRateMultipleThreshold);
-    }();
-
-    // Now we can add the fixed rate layers score
-    for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
-        overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
-        if (maxScoreAboveThreshold) {
-            overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
-        }
-        ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(),
-              overallScore);
-    }
-
-    // Now that we scored all the refresh rates we need to pick the one that got the highest
-    // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers
-    // wanted Max, or the lower otherwise.
-    const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
-            ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
-            : getMaxScoreRefreshRate(scores.begin(), scores.end());
-
-    if (primaryRangeIsSingleRate) {
-        // If we never scored any layers, then choose the rate from the primary
-        // range instead of picking a random score from the app range.
-        if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
-            const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
-            ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
-            return {max, kNoSignals};
-        } else {
-            return {bestRefreshRate, kNoSignals};
-        }
-    }
-
-    // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
-    // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
-    // vote we should not change it if we get a touch event. Only apply touch boost if it will
-    // actually increase the refresh rate over the normal selection.
-    const DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-
-    const bool touchBoostForExplicitExact = [&] {
-        if (mSupportsFrameRateOverrideByContent) {
-            // Enable touch boost if there are other layers besides exact
-            return explicitExact + noVoteLayers != layers.size();
-        } else {
-            // Enable touch boost if there are no exact layers
-            return explicitExact == 0;
-        }
-    }();
-
-    using fps_approx_ops::operator<;
-
-    if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps() < touchRefreshRate->getFps()) {
-        ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str());
-        return {touchRefreshRate, GlobalSignals{.touch = true}};
-    }
-
-    return {bestRefreshRate, kNoSignals};
-}
-
-std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
-groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
-    std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
-    for (const auto& layer : layers) {
-        auto iter = layersByUid.emplace(layer.ownerUid,
-                                        std::vector<const RefreshRateConfigs::LayerRequirement*>());
-        auto& layersWithSameUid = iter.first->second;
-        layersWithSameUid.push_back(&layer);
-    }
-
-    // Remove uids that can't have a frame rate override
-    for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
-        const auto& layersWithSameUid = iter->second;
-        bool skipUid = false;
-        for (const auto& layer : layersWithSameUid) {
-            if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
-                layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
-                skipUid = true;
-                break;
-            }
-        }
-        if (skipUid) {
-            iter = layersByUid.erase(iter);
-        } else {
-            ++iter;
-        }
-    }
-
-    return layersByUid;
-}
-
-RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
-        const std::vector<LayerRequirement>& layers, Fps displayRefreshRate,
-        GlobalSignals globalSignals) const {
-    ATRACE_CALL();
-
-    ALOGV("%s: %zu layers", __func__, layers.size());
-
-    std::lock_guard lock(mLock);
-
-    std::vector<RefreshRateScore> scores;
-    scores.reserve(mDisplayModes.size());
-
-    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
-        scores.emplace_back(RefreshRateScore{it, 0.0f});
-    }
-
-    std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) {
-        const auto& mode1 = lhs.modeIt->second;
-        const auto& mode2 = rhs.modeIt->second;
-        return isStrictlyLess(mode1->getFps(), mode2->getFps());
-    });
-
-    std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
-            groupLayersByUid(layers);
-    UidToFrameRateOverride frameRateOverrides;
-    for (const auto& [uid, layersWithSameUid] : layersByUid) {
-        // Layers with ExplicitExactOrMultiple expect touch boost
-        const bool hasExplicitExactOrMultiple =
-                std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
-                            [](const auto& layer) {
-                                return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
-                            });
-
-        if (globalSignals.touch && hasExplicitExactOrMultiple) {
-            continue;
-        }
-
-        for (auto& [_, score, _1] : scores) {
-            score = 0;
-        }
-
-        for (const auto& layer : layersWithSameUid) {
-            if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
-                continue;
-            }
-
-            LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
-                                layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
-                                layer->vote != LayerVoteType::ExplicitExact);
-            for (auto& [modeIt, score, _] : scores) {
-                constexpr bool isSeamlessSwitch = true;
-                const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
-                                                                  isSeamlessSwitch);
-                score += layer->weight * layerScore;
-            }
-        }
-
-        // We just care about the refresh rates which are a divisor of the
-        // display refresh rate
-        const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) {
-            const auto& [id, mode] = *score.modeIt;
-            return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0;
-        });
-        scores.erase(it, scores.end());
-
-        // If we never scored any layers, we don't have a preferred frame rate
-        if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
-            continue;
-        }
-
-        // Now that we scored all the refresh rates we need to pick the one that got the highest
-        // score.
-        const DisplayModePtr& bestRefreshRate =
-                getMaxScoreRefreshRate(scores.begin(), scores.end());
-
-        frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
-    }
-
-    return frameRateOverrides;
-}
-
-std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
-        std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
-    std::lock_guard lock(mLock);
-
-    const DisplayModePtr& current = desiredActiveModeId
-            ? mDisplayModes.get(*desiredActiveModeId)->get()
-            : mActiveModeIt->second;
-
-    const DisplayModePtr& min = mMinRefreshRateModeIt->second;
-    if (current == min) {
-        return {};
-    }
-
-    const auto& mode = timerExpired ? min : current;
-    return mode->getFps();
-}
-
-const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
-    for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
-        const auto& mode = modeIt->second;
-        if (mActiveModeIt->second->getGroup() == mode->getGroup()) {
-            return mode;
-        }
-    }
-
-    ALOGE("Can't find min refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
-
-    // Default to the lowest refresh rate.
-    return mPrimaryRefreshRates.front()->second;
-}
-
-DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
-    std::lock_guard lock(mLock);
-    return getMaxRefreshRateByPolicyLocked();
-}
-
-const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
-    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
-        const auto& mode = (*it)->second;
-        if (anchorGroup == mode->getGroup()) {
-            return mode;
-        }
-    }
-
-    ALOGE("Can't find max refresh rate by policy with the same mode group"
-          " as the current mode %s",
-          to_string(*mActiveModeIt->second).c_str());
-
-    // Default to the highest refresh rate.
-    return mPrimaryRefreshRates.back()->second;
-}
-
-DisplayModePtr RefreshRateConfigs::getActiveMode() const {
-    std::lock_guard lock(mLock);
-    return mActiveModeIt->second;
-}
-
-void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
-    std::lock_guard lock(mLock);
-
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
-
-    mActiveModeIt = mDisplayModes.find(modeId);
-    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
-}
-
-RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId,
-                                       Config config)
-      : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
-    initializeIdleTimer();
-    updateDisplayModes(std::move(modes), activeModeId);
-}
-
-void RefreshRateConfigs::initializeIdleTimer() {
-    if (mConfig.idleTimerTimeout > 0ms) {
-        mIdleTimer.emplace(
-                "IdleTimer", mConfig.idleTimerTimeout,
-                [this] {
-                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
-                    if (const auto callbacks = getIdleTimerCallbacks()) {
-                        callbacks->onReset();
-                    }
-                },
-                [this] {
-                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
-                    if (const auto callbacks = getIdleTimerCallbacks()) {
-                        callbacks->onExpired();
-                    }
-                });
-    }
-}
-
-void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
-    std::lock_guard lock(mLock);
-
-    // Invalidate the cached invocation to getBestRefreshRate. This forces
-    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
-    mGetBestRefreshRateCache.reset();
-
-    mDisplayModes = std::move(modes);
-    mActiveModeIt = mDisplayModes.find(activeModeId);
-    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
-
-    const auto sortedModes =
-            sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
-    mMinRefreshRateModeIt = sortedModes.front();
-    mMaxRefreshRateModeIt = sortedModes.back();
-
-    // Reset the policy because the old one may no longer be valid.
-    mDisplayManagerPolicy = {};
-    mDisplayManagerPolicy.defaultMode = activeModeId;
-
-    mSupportsFrameRateOverrideByContent =
-            mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes);
-
-    constructAvailableRefreshRates();
-}
-
-bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
-    // defaultMode must be a valid mode, and within the given refresh rate range.
-    if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
-        if (!policy.primaryRange.includes(mode->get()->getFps())) {
-            ALOGE("Default mode is not in the primary range.");
-            return false;
-        }
-    } else {
-        ALOGE("Default mode is not found.");
-        return false;
-    }
-
-    using namespace fps_approx_ops;
-    return policy.appRequestRange.min <= policy.primaryRange.min &&
-            policy.appRequestRange.max >= policy.primaryRange.max;
-}
-
-status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
-    std::lock_guard lock(mLock);
-    if (!isPolicyValidLocked(policy)) {
-        ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
-        return BAD_VALUE;
-    }
-    mGetBestRefreshRateCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mDisplayManagerPolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
-}
-
-status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
-    std::lock_guard lock(mLock);
-    if (policy && !isPolicyValidLocked(*policy)) {
-        return BAD_VALUE;
-    }
-    mGetBestRefreshRateCache.reset();
-    Policy previousPolicy = *getCurrentPolicyLocked();
-    mOverridePolicy = policy;
-    if (*getCurrentPolicyLocked() == previousPolicy) {
-        return CURRENT_POLICY_UNCHANGED;
-    }
-    constructAvailableRefreshRates();
-    return NO_ERROR;
-}
-
-const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
-    return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
-}
-
-RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const {
-    std::lock_guard lock(mLock);
-    return *getCurrentPolicyLocked();
-}
-
-RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const {
-    std::lock_guard lock(mLock);
-    return mDisplayManagerPolicy;
-}
-
-bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
-    std::lock_guard lock(mLock);
-    return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
-                       [modeId](DisplayModeIterator modeIt) {
-                           return modeIt->second->getId() == modeId;
-                       });
-}
-
-void RefreshRateConfigs::constructAvailableRefreshRates() {
-    // Filter modes based on current policy and sort on refresh rate.
-    const Policy* policy = getCurrentPolicyLocked();
-    ALOGV("%s: %s ", __func__, policy->toString().c_str());
-
-    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
-
-    const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
-        const auto filter = [&](const DisplayMode& mode) {
-            return mode.getResolution() == defaultMode->getResolution() &&
-                    mode.getDpi() == defaultMode->getDpi() &&
-                    (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
-                    range.includes(mode.getFps());
-        };
-
-        const auto modes = sortByRefreshRate(mDisplayModes, filter);
-        LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
-                            to_string(range).c_str());
-
-        const auto stringifyModes = [&] {
-            std::string str;
-            for (const auto modeIt : modes) {
-                str += to_string(modeIt->second->getFps());
-                str.push_back(' ');
-            }
-            return str;
-        };
-        ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
-
-        return modes;
-    };
-
-    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary");
-    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
-}
-
-Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
-    using namespace fps_approx_ops;
-
-    if (frameRate <= mKnownFrameRates.front()) {
-        return mKnownFrameRates.front();
-    }
-
-    if (frameRate >= mKnownFrameRates.back()) {
-        return mKnownFrameRates.back();
-    }
-
-    auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
-                                       isStrictlyLess);
-
-    const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
-    const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
-    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
-}
-
-RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
-    std::lock_guard lock(mLock);
-
-    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
-    const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
-
-    // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
-    // the min allowed refresh rate is higher than the device min, we do not want to enable the
-    // timer.
-    if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
-        return KernelIdleTimerAction::TurnOff;
-    }
-
-    const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked();
-    if (minByPolicy == maxByPolicy) {
-        // Turn on the timer when the min of the primary range is below the device min.
-        if (const Policy* currentPolicy = getCurrentPolicyLocked();
-            isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) {
-            return KernelIdleTimerAction::TurnOn;
-        }
-        return KernelIdleTimerAction::TurnOff;
-    }
-
-    // Turn on the timer in all other cases.
-    return KernelIdleTimerAction::TurnOn;
-}
-
-int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
-    // This calculation needs to be in sync with the java code
-    // in DisplayManagerService.getDisplayInfoForFrameRateOverride
-
-    // The threshold must be smaller than 0.001 in order to differentiate
-    // between the fractional pairs (e.g. 59.94 and 60).
-    constexpr float kThreshold = 0.0009f;
-    const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
-    const auto numPeriodsRounded = std::round(numPeriods);
-    if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
-        return 0;
-    }
-
-    return static_cast<int>(numPeriodsRounded);
-}
-
-bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
-    if (isStrictlyLess(bigger, smaller)) {
-        return isFractionalPairOrMultiple(bigger, smaller);
-    }
-
-    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
-    constexpr float kCoef = 1000.f / 1001.f;
-    return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
-            isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
-}
-
-void RefreshRateConfigs::dump(std::string& result) const {
-    using namespace std::string_literals;
-
-    std::lock_guard lock(mLock);
-
-    const auto activeModeId = mActiveModeIt->first;
-    result += "   activeModeId="s;
-    result += std::to_string(activeModeId.value());
-
-    result += "\n   displayModes=\n"s;
-    for (const auto& [id, mode] : mDisplayModes) {
-        result += "      "s;
-        result += to_string(*mode);
-        result += '\n';
-    }
-
-    base::StringAppendF(&result, "   displayManagerPolicy=%s\n",
-                        mDisplayManagerPolicy.toString().c_str());
-
-    if (const Policy& currentPolicy = *getCurrentPolicyLocked();
-        mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
-        base::StringAppendF(&result, "   overridePolicy=%s\n", currentPolicy.toString().c_str());
-    }
-
-    base::StringAppendF(&result, "   supportsFrameRateOverrideByContent=%s\n",
-                        mSupportsFrameRateOverrideByContent ? "true" : "false");
-
-    result += "   idleTimer="s;
-    if (mIdleTimer) {
-        result += mIdleTimer->dump();
-    } else {
-        result += "off"s;
-    }
-
-    if (const auto controller = mConfig.kernelIdleTimerController) {
-        base::StringAppendF(&result, " (kernel via %s)", ftl::enum_string(*controller).c_str());
-    } else {
-        result += " (platform)"s;
-    }
-
-    result += '\n';
-}
-
-std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {
-    return mConfig.idleTimerTimeout;
-}
-
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
deleted file mode 100644
index a79002e..0000000
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ /dev/null
@@ -1,413 +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 <algorithm>
-#include <numeric>
-#include <optional>
-#include <type_traits>
-#include <utility>
-
-#include <gui/DisplayEventReceiver.h>
-
-#include <scheduler/Fps.h>
-#include <scheduler/Seamlessness.h>
-
-#include "DisplayHardware/DisplayMode.h"
-#include "DisplayHardware/HWComposer.h"
-#include "Scheduler/OneShotTimer.h"
-#include "Scheduler/StrongTyping.h"
-
-namespace android::scheduler {
-
-using namespace std::chrono_literals;
-
-enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
-
-inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
-    using T = std::underlying_type_t<DisplayModeEvent>;
-    return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
-}
-
-using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
-
-/**
- * This class is used to encapsulate configuration for refresh rates. It holds information
- * about available refresh rates on the device, and the mapping between the numbers and human
- * readable names.
- */
-class RefreshRateConfigs {
-public:
-    // Margin used when matching refresh rates to the content desired ones.
-    static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
-            std::chrono::nanoseconds(800us).count();
-
-    struct Policy {
-    private:
-        static constexpr int kAllowGroupSwitchingDefault = false;
-
-    public:
-        // The default mode, used to ensure we only initiate display mode switches within the
-        // same mode group as defaultMode's group.
-        DisplayModeId defaultMode;
-        // Whether or not we switch mode groups to get the best frame rate.
-        bool allowGroupSwitching = kAllowGroupSwitchingDefault;
-        // The primary refresh rate range represents display manager's general guidance on the
-        // display modes we'll consider when switching refresh rates. Unless we get an explicit
-        // signal from an app, we should stay within this range.
-        FpsRange primaryRange;
-        // The app request refresh rate range allows us to consider more display modes when
-        // switching refresh rates. Although we should generally stay within the primary range,
-        // specific considerations, such as layer frame rate settings specified via the
-        // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
-        // app request range. The app request range will be greater than or equal to the primary
-        // refresh rate range, never smaller.
-        FpsRange appRequestRange;
-
-        Policy() = default;
-
-        Policy(DisplayModeId defaultMode, FpsRange range)
-              : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
-
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range)
-              : Policy(defaultMode, allowGroupSwitching, range, range) {}
-
-        Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange)
-              : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
-
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange,
-               FpsRange appRequestRange)
-              : defaultMode(defaultMode),
-                allowGroupSwitching(allowGroupSwitching),
-                primaryRange(primaryRange),
-                appRequestRange(appRequestRange) {}
-
-        bool operator==(const Policy& other) const {
-            using namespace fps_approx_ops;
-            return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
-                    appRequestRange == other.appRequestRange &&
-                    allowGroupSwitching == other.allowGroupSwitching;
-        }
-
-        bool operator!=(const Policy& other) const { return !(*this == other); }
-        std::string toString() const;
-    };
-
-    // Return code set*Policy() to indicate the current policy is unchanged.
-    static constexpr int CURRENT_POLICY_UNCHANGED = 1;
-
-    // We maintain the display manager policy and the override policy separately. The override
-    // policy is used by CTS tests to get a consistent device state for testing. While the override
-    // policy is set, it takes precedence over the display manager policy. Once the override policy
-    // is cleared, we revert to using the display manager policy.
-
-    // Sets the display manager policy to choose refresh rates. The return value will be:
-    //   - A negative value if the policy is invalid or another error occurred.
-    //   - NO_ERROR if the policy was successfully updated, and the current policy is different from
-    //     what it was before the call.
-    //   - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
-    //     is the same as it was before the call.
-    status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
-    // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
-    status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
-    // Gets the current policy, which will be the override policy if active, and the display manager
-    // policy otherwise.
-    Policy getCurrentPolicy() const EXCLUDES(mLock);
-    // Gets the display manager policy, regardless of whether an override policy is active.
-    Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
-
-    // Returns true if mode is allowed by the current policy.
-    bool isModeAllowed(DisplayModeId) const EXCLUDES(mLock);
-
-    // Describes the different options the layer voted for refresh rate
-    enum class LayerVoteType {
-        NoVote,          // Doesn't care about the refresh rate
-        Min,             // Minimal refresh rate available
-        Max,             // Maximal refresh rate available
-        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
-        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
-                         // compatibility
-        ExplicitExactOrMultiple, // Specific refresh rate that was provided by the app with
-                                 // ExactOrMultiple compatibility
-        ExplicitExact,           // Specific refresh rate that was provided by the app with
-                                 // Exact compatibility
-
-        ftl_last = ExplicitExact
-    };
-
-    // Captures the layer requirements for a refresh rate. This will be used to determine the
-    // display refresh rate.
-    struct LayerRequirement {
-        // Layer's name. Used for debugging purposes.
-        std::string name;
-        // Layer's owner uid
-        uid_t ownerUid = static_cast<uid_t>(-1);
-        // Layer vote type.
-        LayerVoteType vote = LayerVoteType::NoVote;
-        // Layer's desired refresh rate, if applicable.
-        Fps desiredRefreshRate;
-        // If a seamless mode switch is required.
-        Seamlessness seamlessness = Seamlessness::Default;
-        // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
-        // would have on choosing the refresh rate.
-        float weight = 0.0f;
-        // Whether layer is in focus or not based on WindowManager's state
-        bool focused = false;
-
-        bool operator==(const LayerRequirement& other) const {
-            return name == other.name && vote == other.vote &&
-                    isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
-                    seamlessness == other.seamlessness && weight == other.weight &&
-                    focused == other.focused;
-        }
-
-        bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
-    };
-
-    // Global state describing signals that affect refresh rate choice.
-    struct GlobalSignals {
-        // Whether the user touched the screen recently. Used to apply touch boost.
-        bool touch = false;
-        // True if the system hasn't seen any buffers posted to layers recently.
-        bool idle = false;
-
-        bool operator==(GlobalSignals other) const {
-            return touch == other.touch && idle == other.idle;
-        }
-    };
-
-    // Returns the refresh rate that best fits the given layers, and whether the refresh rate was
-    // chosen based on touch boost and/or idle timer.
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRate(
-            const std::vector<LayerRequirement>&, GlobalSignals) const EXCLUDES(mLock);
-
-    FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
-        std::lock_guard lock(mLock);
-        return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
-    }
-
-    std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
-                                            bool timerExpired) const EXCLUDES(mLock);
-
-    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
-    // uses the primary range, not the app request range.
-    DisplayModePtr getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
-
-    void setActiveModeId(DisplayModeId) EXCLUDES(mLock);
-    DisplayModePtr getActiveMode() const EXCLUDES(mLock);
-
-    // Returns a known frame rate that is the closest to frameRate
-    Fps findClosestKnownFrameRate(Fps frameRate) const;
-
-    enum class KernelIdleTimerController { Sysprop, HwcApi, ftl_last = HwcApi };
-
-    // Configuration flags.
-    struct Config {
-        bool enableFrameRateOverride = false;
-
-        // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
-        // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
-        // no threshold is set.
-        int frameRateMultipleThreshold = 0;
-
-        // The Idle Timer timeout. 0 timeout means no idle timer.
-        std::chrono::milliseconds idleTimerTimeout = 0ms;
-
-        // The controller representing how the kernel idle timer will be configured
-        // either on the HWC api or sysprop.
-        std::optional<KernelIdleTimerController> kernelIdleTimerController;
-    };
-
-    RefreshRateConfigs(DisplayModes, DisplayModeId activeModeId,
-                       Config config = {.enableFrameRateOverride = false,
-                                        .frameRateMultipleThreshold = 0,
-                                        .idleTimerTimeout = 0ms,
-                                        .kernelIdleTimerController = {}});
-
-    RefreshRateConfigs(const RefreshRateConfigs&) = delete;
-    RefreshRateConfigs& operator=(const RefreshRateConfigs&) = delete;
-
-    // Returns whether switching modes (refresh rate or resolution) is possible.
-    // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
-    // differ in resolution.
-    bool canSwitch() const EXCLUDES(mLock) {
-        std::lock_guard lock(mLock);
-        return mDisplayModes.size() > 1;
-    }
-
-    // Class to enumerate options around toggling the kernel timer on and off.
-    enum class KernelIdleTimerAction {
-        TurnOff,  // Turn off the idle timer.
-        TurnOn    // Turn on the idle timer.
-    };
-
-    // Checks whether kernel idle timer should be active depending the policy decisions around
-    // refresh rates.
-    KernelIdleTimerAction getIdleTimerAction() const;
-
-    bool supportsFrameRateOverrideByContent() const { return mSupportsFrameRateOverrideByContent; }
-
-    // Return the display refresh rate divisor to match the layer
-    // frame rate, or 0 if the display refresh rate is not a multiple of the
-    // layer refresh rate.
-    static int getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate);
-
-    // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
-    // for an integer t.
-    static bool isFractionalPairOrMultiple(Fps, Fps);
-
-    using UidToFrameRateOverride = std::map<uid_t, Fps>;
-
-    // Returns the frame rate override for each uid.
-    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
-                                                 Fps displayFrameRate, GlobalSignals) const
-            EXCLUDES(mLock);
-
-    std::optional<KernelIdleTimerController> kernelIdleTimerController() {
-        return mConfig.kernelIdleTimerController;
-    }
-
-    struct IdleTimerCallbacks {
-        struct Callbacks {
-            std::function<void()> onReset;
-            std::function<void()> onExpired;
-        };
-
-        Callbacks platform;
-        Callbacks kernel;
-    };
-
-    void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
-        std::scoped_lock lock(mIdleTimerCallbacksMutex);
-        mIdleTimerCallbacks = std::move(callbacks);
-    }
-
-    void clearIdleTimerCallbacks() EXCLUDES(mIdleTimerCallbacksMutex) {
-        std::scoped_lock lock(mIdleTimerCallbacksMutex);
-        mIdleTimerCallbacks.reset();
-    }
-
-    void startIdleTimer() {
-        if (mIdleTimer) {
-            mIdleTimer->start();
-        }
-    }
-
-    void stopIdleTimer() {
-        if (mIdleTimer) {
-            mIdleTimer->stop();
-        }
-    }
-
-    void resetIdleTimer(bool kernelOnly) {
-        if (!mIdleTimer) {
-            return;
-        }
-        if (kernelOnly && !mConfig.kernelIdleTimerController.has_value()) {
-            return;
-        }
-        mIdleTimer->reset();
-    }
-
-    void dump(std::string& result) const EXCLUDES(mLock);
-
-    std::chrono::milliseconds getIdleTimerTimeout();
-
-private:
-    friend struct TestableRefreshRateConfigs;
-
-    void constructAvailableRefreshRates() REQUIRES(mLock);
-
-    std::pair<DisplayModePtr, GlobalSignals> getBestRefreshRateLocked(
-            const std::vector<LayerRequirement>&, GlobalSignals) const REQUIRES(mLock);
-
-    // Returns number of display frames and remainder when dividing the layer refresh period by
-    // display refresh period.
-    std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
-
-    // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
-    // uses the primary range, not the app request range.
-    const DisplayModePtr& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock);
-
-    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
-    // uses the primary range, not the app request range.
-    const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
-    const DisplayModePtr& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
-        return getMaxRefreshRateByPolicyLocked(mActiveModeIt->second->getGroup());
-    }
-
-    const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
-    bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
-
-    // calculates a score for a layer. Used to determine the display refresh rate
-    // and the frame rate override for certains applications.
-    float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
-                                    bool isSeamlessSwitch) const REQUIRES(mLock);
-
-    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
-            REQUIRES(mLock);
-
-    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock);
-
-    void initializeIdleTimer();
-
-    std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
-            REQUIRES(mIdleTimerCallbacksMutex) {
-        if (!mIdleTimerCallbacks) return {};
-        return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
-                                                             : mIdleTimerCallbacks->platform;
-    }
-
-    // The display modes of the active display. The DisplayModeIterators below are pointers into
-    // this container, so must be invalidated whenever the DisplayModes change. The Policy below
-    // is also dependent, so must be reset as well.
-    DisplayModes mDisplayModes GUARDED_BY(mLock);
-
-    DisplayModeIterator mActiveModeIt GUARDED_BY(mLock);
-    DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
-    DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
-
-    // Display modes that satisfy the Policy's ranges, filtered and sorted by refresh rate.
-    std::vector<DisplayModeIterator> mPrimaryRefreshRates GUARDED_BY(mLock);
-    std::vector<DisplayModeIterator> mAppRequestRefreshRates GUARDED_BY(mLock);
-
-    Policy mDisplayManagerPolicy GUARDED_BY(mLock);
-    std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
-
-    mutable std::mutex mLock;
-
-    // A sorted list of known frame rates that a Heuristic layer will choose
-    // from based on the closest value.
-    const std::vector<Fps> mKnownFrameRates;
-
-    const Config mConfig;
-    bool mSupportsFrameRateOverrideByContent;
-
-    struct GetBestRefreshRateCache {
-        std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
-        std::pair<DisplayModePtr, GlobalSignals> result;
-    };
-    mutable std::optional<GetBestRefreshRateCache> mGetBestRefreshRateCache GUARDED_BY(mLock);
-
-    // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
-    std::mutex mIdleTimerCallbacksMutex;
-    std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
-    // Used to detect (lack of) frame activity.
-    std::optional<scheduler::OneShotTimer> mIdleTimer;
-};
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
new file mode 100644
index 0000000..f136e9f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -0,0 +1,1362 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <chrono>
+#include <cmath>
+#include <deque>
+#include <map>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
+#include <ftl/unit.h>
+#include <gui/TraceUtils.h>
+#include <scheduler/FrameRateMode.h>
+#include <utils/Trace.h>
+
+#include "../SurfaceFlingerProperties.h"
+#include "RefreshRateSelector.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateSelector"
+
+namespace android::scheduler {
+namespace {
+
+struct RefreshRateScore {
+    FrameRateMode frameRateMode;
+    float overallScore;
+    struct {
+        float modeBelowThreshold;
+        float modeAboveThreshold;
+    } fixedRateBelowThresholdLayersScore;
+};
+
+constexpr RefreshRateSelector::GlobalSignals kNoSignals;
+
+std::string formatLayerInfo(const RefreshRateSelector::LayerRequirement& layer, float weight) {
+    return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
+                              ftl::enum_string(layer.vote).c_str(), weight,
+                              ftl::enum_string(layer.seamlessness).c_str(),
+                              to_string(layer.desiredRefreshRate).c_str());
+}
+
+std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
+    std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
+    knownFrameRates.reserve(knownFrameRates.size() + modes.size());
+
+    // Add all supported refresh rates.
+    for (const auto& [id, mode] : modes) {
+        knownFrameRates.push_back(mode->getFps());
+    }
+
+    // Sort and remove duplicates.
+    std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
+    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+                                      isApproxEqual),
+                          knownFrameRates.end());
+    return knownFrameRates;
+}
+
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes) {
+    std::vector<DisplayModeIterator> sortedModes;
+    sortedModes.reserve(modes.size());
+    for (auto it = modes.begin(); it != modes.end(); ++it) {
+        sortedModes.push_back(it);
+    }
+
+    std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
+        const auto& mode1 = it1->second;
+        const auto& mode2 = it2->second;
+
+        if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
+            return mode1->getGroup() > mode2->getGroup();
+        }
+
+        return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
+    });
+
+    return sortedModes;
+}
+
+std::pair<unsigned, unsigned> divisorRange(Fps fps, FpsRange range,
+                                           RefreshRateSelector::Config::FrameRateOverride config) {
+    if (config != RefreshRateSelector::Config::FrameRateOverride::Enabled) {
+        return {1, 1};
+    }
+
+    using fps_approx_ops::operator/;
+    // use signed type as `fps / range.max` might be 0
+    const auto start = std::max(1, static_cast<int>(fps / range.max) - 1);
+    const auto end = fps /
+            std::max(range.min, RefreshRateSelector::kMinSupportedFrameRate,
+                     fps_approx_ops::operator<);
+
+    return {start, end};
+}
+
+bool shouldEnableFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
+    for (const auto it1 : sortedModes) {
+        const auto& mode1 = it1->second;
+        for (const auto it2 : sortedModes) {
+            const auto& mode2 = it2->second;
+
+            if (RefreshRateSelector::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+std::string toString(const RefreshRateSelector::PolicyVariant& policy) {
+    using namespace std::string_literals;
+
+    return ftl::match(
+            policy,
+            [](const RefreshRateSelector::DisplayManagerPolicy& policy) {
+                return "DisplayManagerPolicy"s + policy.toString();
+            },
+            [](const RefreshRateSelector::OverridePolicy& policy) {
+                return "OverridePolicy"s + policy.toString();
+            },
+            [](RefreshRateSelector::NoOverridePolicy) { return "NoOverridePolicy"s; });
+}
+
+} // namespace
+
+auto RefreshRateSelector::createFrameRateModes(
+        std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
+        -> std::vector<FrameRateMode> {
+    struct Key {
+        Fps fps;
+        int32_t group;
+    };
+
+    struct KeyLess {
+        bool operator()(const Key& a, const Key& b) const {
+            using namespace fps_approx_ops;
+            if (a.fps != b.fps) {
+                return a.fps < b.fps;
+            }
+
+            // For the same fps the order doesn't really matter, but we still
+            // want the behaviour of a strictly less operator.
+            // We use the group id as the secondary ordering for that.
+            return a.group < b.group;
+        }
+    };
+
+    std::map<Key, DisplayModeIterator, KeyLess> ratesMap;
+    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+        const auto& [id, mode] = *it;
+
+        if (!filterModes(*mode)) {
+            continue;
+        }
+        const auto [start, end] =
+                divisorRange(mode->getFps(), renderRange, mConfig.enableFrameRateOverride);
+        for (auto divisor = start; divisor <= end; divisor++) {
+            const auto fps = mode->getFps() / divisor;
+            using fps_approx_ops::operator<;
+            if (divisor > 1 && fps < kMinSupportedFrameRate) {
+                break;
+            }
+
+            if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Enabled &&
+                !renderRange.includes(fps)) {
+                continue;
+            }
+
+            if (mConfig.enableFrameRateOverride ==
+                        Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+                !isNativeRefreshRate(fps)) {
+                continue;
+            }
+
+            const auto [existingIter, emplaceHappened] =
+                    ratesMap.try_emplace(Key{fps, mode->getGroup()}, it);
+            if (emplaceHappened) {
+                ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
+                      to_string(mode->getFps()).c_str());
+            } else {
+                // We might need to update the map as we found a lower refresh rate
+                if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+                    existingIter->second = it;
+                    ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
+                          to_string(mode->getFps()).c_str());
+                }
+            }
+        }
+    }
+
+    std::vector<FrameRateMode> frameRateModes;
+    frameRateModes.reserve(ratesMap.size());
+    for (const auto& [key, mode] : ratesMap) {
+        frameRateModes.emplace_back(FrameRateMode{key.fps, ftl::as_non_null(mode->second)});
+    }
+
+    // We always want that the lowest frame rate will be corresponding to the
+    // lowest mode for power saving.
+    const auto lowestRefreshRateIt =
+            std::min_element(frameRateModes.begin(), frameRateModes.end(),
+                             [](const FrameRateMode& lhs, const FrameRateMode& rhs) {
+                                 return isStrictlyLess(lhs.modePtr->getFps(),
+                                                       rhs.modePtr->getFps());
+                             });
+    frameRateModes.erase(frameRateModes.begin(), lowestRefreshRateIt);
+
+    return frameRateModes;
+}
+
+struct RefreshRateSelector::RefreshRateScoreComparator {
+    bool operator()(const RefreshRateScore& lhs, const RefreshRateScore& rhs) const {
+        const auto& [frameRateMode, overallScore, _] = lhs;
+
+        std::string name = to_string(frameRateMode);
+
+        ALOGV("%s sorting scores %.2f", name.c_str(), overallScore);
+
+        if (!ScoredFrameRate::scoresEqual(overallScore, rhs.overallScore)) {
+            return overallScore > rhs.overallScore;
+        }
+
+        if (refreshRateOrder == RefreshRateOrder::Descending) {
+            using fps_approx_ops::operator>;
+            return frameRateMode.fps > rhs.frameRateMode.fps;
+        } else {
+            using fps_approx_ops::operator<;
+            return frameRateMode.fps < rhs.frameRateMode.fps;
+        }
+    }
+
+    const RefreshRateOrder refreshRateOrder;
+};
+
+std::string RefreshRateSelector::Policy::toString() const {
+    return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
+                              ", primaryRanges=%s, appRequestRanges=%s}",
+                              defaultMode.value(), allowGroupSwitching ? "true" : "false",
+                              to_string(primaryRanges).c_str(),
+                              to_string(appRequestRanges).c_str());
+}
+
+std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod,
+                                                                  nsecs_t displayPeriod) const {
+    auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+    if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+        std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+        quotient++;
+        remainder = 0;
+    }
+
+    return {quotient, remainder};
+}
+
+float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
+                                                                     Fps refreshRate) const {
+    constexpr float kScoreForFractionalPairs = .8f;
+
+    const auto displayPeriod = refreshRate.getPeriodNsecs();
+    const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+    if (layer.vote == LayerVoteType::ExplicitDefault) {
+        // Find the actual rate the layer will render, assuming
+        // that layerPeriod is the minimal period to render a frame.
+        // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+        // then the actualLayerPeriod will be 32ms, because it is the
+        // smallest multiple of the display period which is >= layerPeriod.
+        auto actualLayerPeriod = displayPeriod;
+        int multiplier = 1;
+        while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+            multiplier++;
+            actualLayerPeriod = displayPeriod * multiplier;
+        }
+
+        // Because of the threshold we used above it's possible that score is slightly
+        // above 1.
+        return std::min(1.0f,
+                        static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+    }
+
+    if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+        layer.vote == LayerVoteType::Heuristic) {
+        const float multiplier = refreshRate.getValue() / layer.desiredRefreshRate.getValue();
+
+        // We only want to score this layer as a fractional pair if the content is not
+        // significantly faster than the display rate, at it would cause a significant frame drop.
+        // It is more appropriate to choose a higher display rate even if
+        // a pull-down will be required.
+        constexpr float kMinMultiplier = 0.75f;
+        if (multiplier >= kMinMultiplier &&
+            isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
+            return kScoreForFractionalPairs;
+        }
+
+        // Calculate how many display vsyncs we need to present a single frame for this
+        // layer
+        const auto [displayFramesQuotient, displayFramesRemainder] =
+                getDisplayFrames(layerPeriod, displayPeriod);
+        static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+        if (displayFramesRemainder == 0) {
+            // Layer desired refresh rate matches the display rate.
+            return 1.0f;
+        }
+
+        if (displayFramesQuotient == 0) {
+            // Layer desired refresh rate is higher than the display rate.
+            return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                    (1.0f / (MAX_FRAMES_TO_FIT + 1));
+        }
+
+        // Layer desired refresh rate is lower than the display rate. Check how well it fits
+        // the cadence.
+        auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+        int iter = 2;
+        while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+            diff = diff - (displayPeriod - diff);
+            iter++;
+        }
+
+        return (1.0f / iter);
+    }
+
+    return 0;
+}
+
+float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const {
+    const auto& maxFps = mAppRequestFrameRates.back().fps;
+    const float ratio = refreshRate.getValue() / maxFps.getValue();
+    // Use ratio^2 to get a lower score the more we get further from peak
+    return ratio * ratio;
+}
+
+float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
+                                                     bool isSeamlessSwitch) const {
+    ATRACE_CALL();
+    // Slightly prefer seamless switches.
+    constexpr float kSeamedSwitchPenalty = 0.95f;
+    const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+    // If the layer wants Max, give higher score to the higher refresh rate
+    if (layer.vote == LayerVoteType::Max) {
+        return calculateDistanceScoreFromMax(refreshRate);
+    }
+
+    if (layer.vote == LayerVoteType::ExplicitExact) {
+        const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
+        if (supportsAppFrameRateOverrideByContent()) {
+            // Since we support frame rate override, allow refresh rates which are
+            // multiples of the layer's request, as those apps would be throttled
+            // down to run at the desired refresh rate.
+            return divisor > 0;
+        }
+
+        return divisor == 1;
+    }
+
+    // If the layer frame rate is a divisor of the refresh rate it should score
+    // the highest score.
+    if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
+        return 1.0f * seamlessness;
+    }
+
+    // The layer frame rate is not a divisor of the refresh rate,
+    // there is a small penalty attached to the score to favor the frame rates
+    // the exactly matches the display refresh rate or a multiple.
+    constexpr float kNonExactMatchingPenalty = 0.95f;
+    return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+            kNonExactMatchingPenalty;
+}
+
+auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+                                              GlobalSignals signals) const -> RankedFrameRates {
+    std::lock_guard lock(mLock);
+
+    if (mGetRankedFrameRatesCache &&
+        mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+        return mGetRankedFrameRatesCache->result;
+    }
+
+    const auto result = getRankedFrameRatesLocked(layers, signals);
+    mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
+    return result;
+}
+
+auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+                                                    GlobalSignals signals) const
+        -> RankedFrameRates {
+    using namespace fps_approx_ops;
+    ATRACE_CALL();
+    ALOGV("%s: %zu layers", __func__, layers.size());
+
+    const auto& activeMode = *getActiveModeLocked().modePtr;
+
+    // Keep the display at max frame rate for the duration of powering on the display.
+    if (signals.powerOnImminent) {
+        ALOGV("Power On Imminent");
+        const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending);
+        ATRACE_FORMAT_INSTANT("%s (Power On Imminent)",
+                              to_string(ranking.front().frameRateMode.fps).c_str());
+        return {ranking, GlobalSignals{.powerOnImminent = true}};
+    }
+
+    int noVoteLayers = 0;
+    int minVoteLayers = 0;
+    int maxVoteLayers = 0;
+    int explicitDefaultVoteLayers = 0;
+    int explicitExactOrMultipleVoteLayers = 0;
+    int explicitExact = 0;
+    int seamedFocusedLayers = 0;
+
+    for (const auto& layer : layers) {
+        switch (layer.vote) {
+            case LayerVoteType::NoVote:
+                noVoteLayers++;
+                break;
+            case LayerVoteType::Min:
+                minVoteLayers++;
+                break;
+            case LayerVoteType::Max:
+                maxVoteLayers++;
+                break;
+            case LayerVoteType::ExplicitDefault:
+                explicitDefaultVoteLayers++;
+                break;
+            case LayerVoteType::ExplicitExactOrMultiple:
+                explicitExactOrMultipleVoteLayers++;
+                break;
+            case LayerVoteType::ExplicitExact:
+                explicitExact++;
+                break;
+            case LayerVoteType::Heuristic:
+                break;
+        }
+
+        if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+            seamedFocusedLayers++;
+        }
+    }
+
+    const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
+            explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+
+    const Policy* policy = getCurrentPolicyLocked();
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
+    // If the default mode group is different from the group of current mode,
+    // this means a layer requesting a seamed mode switch just disappeared and
+    // we should switch back to the default group.
+    // However if a seamed layer is still present we anchor around the group
+    // of the current mode, in order to prevent unnecessary seamed mode switches
+    // (e.g. when pausing a video playback).
+    const auto anchorGroup =
+            seamedFocusedLayers > 0 ? activeMode.getGroup() : defaultMode->getGroup();
+
+    // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
+    // selected a refresh rate to see if we should apply touch boost.
+    if (signals.touch && !hasExplicitVoteLayers) {
+        ALOGV("Touch Boost");
+        const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+        ATRACE_FORMAT_INSTANT("%s (Touch Boost)",
+                              to_string(ranking.front().frameRateMode.fps).c_str());
+        return {ranking, GlobalSignals{.touch = true}};
+    }
+
+    // If the primary range consists of a single refresh rate then we can only
+    // move out the of range if layers explicitly request a different refresh
+    // rate.
+    const bool primaryRangeIsSingleRate =
+            isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max);
+
+    if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+        ALOGV("Idle");
+        const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
+        ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str());
+        return {ranking, GlobalSignals{.idle = true}};
+    }
+
+    if (layers.empty() || noVoteLayers == layers.size()) {
+        ALOGV("No layers with votes");
+        const auto ranking = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+        ATRACE_FORMAT_INSTANT("%s (No layers with votes)",
+                              to_string(ranking.front().frameRateMode.fps).c_str());
+        return {ranking, kNoSignals};
+    }
+
+    // Only if all layers want Min we should return Min
+    if (noVoteLayers + minVoteLayers == layers.size()) {
+        ALOGV("All layers Min");
+        const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
+        ATRACE_FORMAT_INSTANT("%s (All layers Min)",
+                              to_string(ranking.front().frameRateMode.fps).c_str());
+        return {ranking, kNoSignals};
+    }
+
+    // Find the best refresh rate based on score
+    std::vector<RefreshRateScore> scores;
+    scores.reserve(mAppRequestFrameRates.size());
+
+    for (const FrameRateMode& it : mAppRequestFrameRates) {
+        scores.emplace_back(RefreshRateScore{it, 0.0f});
+    }
+
+    for (const auto& layer : layers) {
+        ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
+              ftl::enum_string(layer.vote).c_str(), layer.weight,
+              layer.desiredRefreshRate.getValue());
+        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
+            continue;
+        }
+
+        const auto weight = layer.weight;
+
+        for (auto& [mode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+            const auto& [fps, modePtr] = mode;
+            const bool isSeamlessSwitch = modePtr->getGroup() == activeMode.getGroup();
+
+            if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
+                ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+                      to_string(activeMode).c_str());
+                continue;
+            }
+
+            if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
+                !layer.focused) {
+                ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
+                      " Current mode = %s",
+                      formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(),
+                      to_string(activeMode).c_str());
+                continue;
+            }
+
+            // Layers with default seamlessness vote for the current mode group if
+            // there are layers with seamlessness=SeamedAndSeamless and for the default
+            // mode group otherwise. In second case, if the current mode group is different
+            // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
+            // disappeared.
+            const bool isInPolicyForDefault = modePtr->getGroup() == anchorGroup;
+            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
+                ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
+                      to_string(*modePtr).c_str(), to_string(activeMode).c_str());
+                continue;
+            }
+
+            const bool inPrimaryRange = policy->primaryRanges.render.includes(fps);
+            if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+                !(layer.focused &&
+                  (layer.vote == LayerVoteType::ExplicitDefault ||
+                   layer.vote == LayerVoteType::ExplicitExact))) {
+                // Only focused layers with ExplicitDefault frame rate settings are allowed to score
+                // refresh rates outside the primary range.
+                continue;
+            }
+
+            const float layerScore = calculateLayerScoreLocked(layer, fps, isSeamlessSwitch);
+            const float weightedLayerScore = weight * layerScore;
+
+            // Layer with fixed source has a special consideration which depends on the
+            // mConfig.frameRateMultipleThreshold. We don't want these layers to score
+            // refresh rates above the threshold, but we also don't want to favor the lower
+            // ones by having a greater number of layers scoring them. Instead, we calculate
+            // the score independently for these layers and later decide which
+            // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not
+            // score 120 Hz, but desired 60 fps should contribute to the score.
+            const bool fixedSourceLayer = [](LayerVoteType vote) {
+                switch (vote) {
+                    case LayerVoteType::ExplicitExactOrMultiple:
+                    case LayerVoteType::Heuristic:
+                        return true;
+                    case LayerVoteType::NoVote:
+                    case LayerVoteType::Min:
+                    case LayerVoteType::Max:
+                    case LayerVoteType::ExplicitDefault:
+                    case LayerVoteType::ExplicitExact:
+                        return false;
+                }
+            }(layer.vote);
+            const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 &&
+                    layer.desiredRefreshRate <
+                            Fps::fromValue(mConfig.frameRateMultipleThreshold / 2);
+            if (fixedSourceLayer && layerBelowThreshold) {
+                const bool modeAboveThreshold =
+                        modePtr->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold);
+                if (modeAboveThreshold) {
+                    ALOGV("%s gives %s (%s) fixed source (above threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+                          to_string(modePtr->getFps()).c_str(), layerScore);
+                    fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore;
+                } else {
+                    ALOGV("%s gives %s (%s) fixed source (below threshold) score of %.4f",
+                          formatLayerInfo(layer, weight).c_str(), to_string(fps).c_str(),
+                          to_string(modePtr->getFps()).c_str(), layerScore);
+                    fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore;
+                }
+            } else {
+                ALOGV("%s gives %s (%s) score of %.4f", formatLayerInfo(layer, weight).c_str(),
+                      to_string(fps).c_str(), to_string(modePtr->getFps()).c_str(), layerScore);
+                overallScore += weightedLayerScore;
+            }
+        }
+    }
+
+    // We want to find the best refresh rate without the fixed source layers,
+    // so we could know whether we should add the modeAboveThreshold scores or not.
+    // If the best refresh rate is already above the threshold, it means that
+    // some non-fixed source layers already scored it, so we can just add the score
+    // for all fixed source layers, even the ones that are above the threshold.
+    const bool maxScoreAboveThreshold = [&] {
+        if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) {
+            return false;
+        }
+
+        const auto maxScoreIt =
+                std::max_element(scores.begin(), scores.end(),
+                                 [](RefreshRateScore max, RefreshRateScore current) {
+                                     return current.overallScore > max.overallScore;
+                                 });
+        ALOGV("%s (%s) is the best refresh rate without fixed source layers. It is %s the "
+              "threshold for "
+              "refresh rate multiples",
+              to_string(maxScoreIt->frameRateMode.fps).c_str(),
+              to_string(maxScoreIt->frameRateMode.modePtr->getFps()).c_str(),
+              maxScoreAboveThreshold ? "above" : "below");
+        return maxScoreIt->frameRateMode.modePtr->getFps() >=
+                Fps::fromValue(mConfig.frameRateMultipleThreshold);
+    }();
+
+    // Now we can add the fixed rate layers score
+    for (auto& [frameRateMode, overallScore, fixedRateBelowThresholdLayersScore] : scores) {
+        overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold;
+        if (maxScoreAboveThreshold) {
+            overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold;
+        }
+        ALOGV("%s (%s) adjusted overallScore is %.4f", to_string(frameRateMode.fps).c_str(),
+              to_string(frameRateMode.modePtr->getFps()).c_str(), overallScore);
+    }
+
+    // Now that we scored all the refresh rates we need to pick the one that got the highest
+    // overallScore. Sort the scores based on their overallScore in descending order of priority.
+    const RefreshRateOrder refreshRateOrder =
+            maxVoteLayers > 0 ? RefreshRateOrder::Descending : RefreshRateOrder::Ascending;
+    std::sort(scores.begin(), scores.end(),
+              RefreshRateScoreComparator{.refreshRateOrder = refreshRateOrder});
+
+    FrameRateRanking ranking;
+    ranking.reserve(scores.size());
+
+    std::transform(scores.begin(), scores.end(), back_inserter(ranking),
+                   [](const RefreshRateScore& score) {
+                       return ScoredFrameRate{score.frameRateMode, score.overallScore};
+                   });
+
+    const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
+        return score.overallScore == 0;
+    });
+
+    if (primaryRangeIsSingleRate) {
+        // If we never scored any layers, then choose the rate from the primary
+        // range instead of picking a random score from the app range.
+        if (noLayerScore) {
+            ALOGV("Layers not scored");
+            const auto descending = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+            ATRACE_FORMAT_INSTANT("%s (Layers not scored)",
+                                  to_string(descending.front().frameRateMode.fps).c_str());
+            return {descending, kNoSignals};
+        } else {
+            ATRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)",
+                                  to_string(ranking.front().frameRateMode.fps).c_str());
+            return {ranking, kNoSignals};
+        }
+    }
+
+    // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
+    // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
+    // vote we should not change it if we get a touch event. Only apply touch boost if it will
+    // actually increase the refresh rate over the normal selection.
+    const bool touchBoostForExplicitExact = [&] {
+        if (supportsAppFrameRateOverrideByContent()) {
+            // Enable touch boost if there are other layers besides exact
+            return explicitExact + noVoteLayers != layers.size();
+        } else {
+            // Enable touch boost if there are no exact layers
+            return explicitExact == 0;
+        }
+    }();
+
+    const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+    using fps_approx_ops::operator<;
+
+    if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+        scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
+        ALOGV("Touch Boost");
+        ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
+                              to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
+        return {touchRefreshRates, GlobalSignals{.touch = true}};
+    }
+
+    // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
+    // current config
+    if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+        const auto ascendingWithPreferred =
+                rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
+        ATRACE_FORMAT_INSTANT("%s (preferredDisplayMode)",
+                              to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
+        return {ascendingWithPreferred, kNoSignals};
+    }
+
+    ATRACE_FORMAT_INSTANT("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
+    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
+        -> UidToFrameRateOverride {
+    ATRACE_CALL();
+    if (mConfig.enableFrameRateOverride == Config::FrameRateOverride::Disabled) {
+        return {};
+    }
+
+    ALOGV("%s: %zu layers", __func__, layers.size());
+    std::lock_guard lock(mLock);
+
+    const auto* policyPtr = getCurrentPolicyLocked();
+    // We don't want to run lower than 30fps
+    const Fps minFrameRate = std::max(policyPtr->appRequestRanges.render.min, 30_Hz, isApproxLess);
+
+    using fps_approx_ops::operator/;
+    const unsigned numMultiples = displayRefreshRate / minFrameRate;
+
+    std::vector<std::pair<Fps, float>> scoredFrameRates;
+    scoredFrameRates.reserve(numMultiples);
+
+    for (unsigned n = numMultiples; n > 0; n--) {
+        const Fps divisor = displayRefreshRate / n;
+        if (mConfig.enableFrameRateOverride ==
+                    Config::FrameRateOverride::AppOverrideNativeRefreshRates &&
+            !isNativeRefreshRate(divisor)) {
+            continue;
+        }
+
+        if (policyPtr->appRequestRanges.render.includes(divisor)) {
+            ALOGV("%s: adding %s as a potential frame rate", __func__, to_string(divisor).c_str());
+            scoredFrameRates.emplace_back(divisor, 0);
+        }
+    }
+
+    const auto layersByUid = groupLayersByUid(layers);
+    UidToFrameRateOverride frameRateOverrides;
+    for (const auto& [uid, layersWithSameUid] : layersByUid) {
+        // Layers with ExplicitExactOrMultiple expect touch boost
+        const bool hasExplicitExactOrMultiple =
+                std::any_of(layersWithSameUid.cbegin(), layersWithSameUid.cend(),
+                            [](const auto& layer) {
+                                return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
+                            });
+
+        if (globalSignals.touch && hasExplicitExactOrMultiple) {
+            continue;
+        }
+
+        for (auto& [_, score] : scoredFrameRates) {
+            score = 0;
+        }
+
+        for (const auto& layer : layersWithSameUid) {
+            if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+                continue;
+            }
+
+            LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+                                layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
+                                layer->vote != LayerVoteType::ExplicitExact);
+            for (auto& [fps, score] : scoredFrameRates) {
+                constexpr bool isSeamlessSwitch = true;
+                const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
+                score += layer->weight * layerScore;
+            }
+        }
+
+        // If we never scored any layers, we don't have a preferred frame rate
+        if (std::all_of(scoredFrameRates.begin(), scoredFrameRates.end(),
+                        [](const auto& scoredFrameRate) {
+                            const auto [_, score] = scoredFrameRate;
+                            return score == 0;
+                        })) {
+            continue;
+        }
+
+        // Now that we scored all the refresh rates we need to pick the lowest refresh rate
+        // that got the highest score.
+        const auto [overrideFps, _] =
+                *std::max_element(scoredFrameRates.begin(), scoredFrameRates.end(),
+                                  [](const auto& lhsPair, const auto& rhsPair) {
+                                      const float lhs = lhsPair.second;
+                                      const float rhs = rhsPair.second;
+                                      return lhs < rhs && !ScoredFrameRate::scoresEqual(lhs, rhs);
+                                  });
+        ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
+        frameRateOverrides.emplace(uid, overrideFps);
+    }
+
+    return frameRateOverrides;
+}
+
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
+        std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
+    std::lock_guard lock(mLock);
+
+    const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+        if (desiredActiveModeId) {
+            const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+            return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+        }
+
+        return getActiveModeLocked();
+    }();
+
+    const DisplayModePtr& min = mMinRefreshRateModeIt->second;
+    if (current.modePtr->getId() == min->getId()) {
+        return {};
+    }
+
+    return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
+}
+
+const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
+    const auto& activeMode = *getActiveModeLocked().modePtr;
+
+    for (const FrameRateMode& mode : mPrimaryFrameRates) {
+        if (activeMode.getGroup() == mode.modePtr->getGroup()) {
+            return mode.modePtr.get();
+        }
+    }
+
+    ALOGE("Can't find min refresh rate by policy with the same mode group as the current mode %s",
+          to_string(activeMode).c_str());
+
+    // Default to the lowest refresh rate.
+    return mPrimaryFrameRates.front().modePtr.get();
+}
+
+const DisplayModePtr& RefreshRateSelector::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
+    const ftl::NonNull<DisplayModePtr>* maxByAnchor = &mPrimaryFrameRates.back().modePtr;
+    const ftl::NonNull<DisplayModePtr>* max = &mPrimaryFrameRates.back().modePtr;
+
+    bool maxByAnchorFound = false;
+    for (auto it = mPrimaryFrameRates.rbegin(); it != mPrimaryFrameRates.rend(); ++it) {
+        using namespace fps_approx_ops;
+        if (it->modePtr->getFps() > (*max)->getFps()) {
+            max = &it->modePtr;
+        }
+
+        if (anchorGroup == it->modePtr->getGroup() &&
+            it->modePtr->getFps() >= (*maxByAnchor)->getFps()) {
+            maxByAnchorFound = true;
+            maxByAnchor = &it->modePtr;
+        }
+    }
+
+    if (maxByAnchorFound) {
+        return maxByAnchor->get();
+    }
+
+    ALOGE("Can't find max refresh rate by policy with the same group %d", anchorGroup);
+
+    // Default to the highest refresh rate.
+    return max->get();
+}
+
+auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt,
+                                         RefreshRateOrder refreshRateOrder,
+                                         std::optional<DisplayModeId> preferredDisplayModeOpt) const
+        -> FrameRateRanking {
+    using fps_approx_ops::operator<;
+    const char* const whence = __func__;
+
+    // find the highest frame rate for each display mode
+    ftl::SmallMap<DisplayModeId, Fps, 8> maxRenderRateForMode;
+    const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
+    if (ascending) {
+        // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
+        //  use a lower frame rate when we want Ascending frame rates.
+        for (const auto& frameRateMode : mPrimaryFrameRates) {
+            if (anchorGroupOpt && frameRateMode.modePtr->getGroup() != anchorGroupOpt) {
+                continue;
+            }
+
+            const auto [iter, _] = maxRenderRateForMode.try_emplace(frameRateMode.modePtr->getId(),
+                                                                    frameRateMode.fps);
+            if (iter->second < frameRateMode.fps) {
+                iter->second = frameRateMode.fps;
+            }
+        }
+    }
+
+    std::deque<ScoredFrameRate> ranking;
+    const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+        const auto& modePtr = frameRateMode.modePtr;
+        if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
+            return;
+        }
+
+        const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
+        const auto id = modePtr->getId();
+        if (ascending && frameRateMode.fps < *maxRenderRateForMode.get(id)) {
+            // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
+            //  use a lower frame rate when we want Ascending frame rates.
+            return;
+        }
+
+        float score = calculateDistanceScoreFromMax(frameRateMode.fps);
+
+        if (ascending) {
+            score = 1.0f / score;
+        }
+
+        constexpr float kScore = std::numeric_limits<float>::max();
+        if (preferredDisplayModeOpt) {
+            if (*preferredDisplayModeOpt == modePtr->getId()) {
+                ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
+                return;
+            }
+            constexpr float kNonPreferredModePenalty = 0.95f;
+            score *= kNonPreferredModePenalty;
+        } else if (ascending && id == getMinRefreshRateByPolicyLocked()->getId()) {
+            // TODO(b/266481656): Once this bug is fixed, we can remove this workaround
+            //  and actually use a lower frame rate when we want Ascending frame rates.
+            ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore});
+            return;
+        }
+
+        ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
+              to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
+        ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
+    };
+
+    if (refreshRateOrder == RefreshRateOrder::Ascending) {
+        std::for_each(mPrimaryFrameRates.begin(), mPrimaryFrameRates.end(), rankFrameRate);
+    } else {
+        std::for_each(mPrimaryFrameRates.rbegin(), mPrimaryFrameRates.rend(), rankFrameRate);
+    }
+
+    if (!ranking.empty() || !anchorGroupOpt) {
+        return {ranking.begin(), ranking.end()};
+    }
+
+    ALOGW("Can't find %s refresh rate by policy with the same mode group"
+          " as the mode group %d",
+          refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
+
+    constexpr std::optional<int> kNoAnchorGroup = std::nullopt;
+    return rankFrameRates(kNoAnchorGroup, refreshRateOrder, preferredDisplayModeOpt);
+}
+
+FrameRateMode RefreshRateSelector::getActiveMode() const {
+    std::lock_guard lock(mLock);
+    return getActiveModeLocked();
+}
+
+const FrameRateMode& RefreshRateSelector::getActiveModeLocked() const {
+    return *mActiveModeOpt;
+}
+
+void RefreshRateSelector::setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
+    std::lock_guard lock(mLock);
+
+    // Invalidate the cached invocation to getRankedFrameRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+    mGetRankedFrameRatesCache.reset();
+
+    const auto activeModeOpt = mDisplayModes.get(modeId);
+    LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+
+    mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())});
+}
+
+RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
+                                         Config config)
+      : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
+    initializeIdleTimer();
+    FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
+}
+
+void RefreshRateSelector::initializeIdleTimer() {
+    if (mConfig.idleTimerTimeout > 0ms) {
+        mIdleTimer.emplace(
+                "IdleTimer", mConfig.idleTimerTimeout,
+                [this] {
+                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
+                    if (const auto callbacks = getIdleTimerCallbacks()) {
+                        callbacks->onReset();
+                    }
+                },
+                [this] {
+                    std::scoped_lock lock(mIdleTimerCallbacksMutex);
+                    if (const auto callbacks = getIdleTimerCallbacks()) {
+                        callbacks->onExpired();
+                    }
+                });
+    }
+}
+
+void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
+    std::lock_guard lock(mLock);
+
+    // Invalidate the cached invocation to getRankedFrameRates. This forces
+    // the refresh rate to be recomputed on the next call to getRankedFrameRates.
+    mGetRankedFrameRatesCache.reset();
+
+    mDisplayModes = std::move(modes);
+    const auto activeModeOpt = mDisplayModes.get(activeModeId);
+    LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+    mActiveModeOpt =
+            FrameRateMode{activeModeOpt->get()->getFps(), ftl::as_non_null(activeModeOpt->get())};
+
+    const auto sortedModes = sortByRefreshRate(mDisplayModes);
+    mMinRefreshRateModeIt = sortedModes.front();
+    mMaxRefreshRateModeIt = sortedModes.back();
+
+    // Reset the policy because the old one may no longer be valid.
+    mDisplayManagerPolicy = {};
+    mDisplayManagerPolicy.defaultMode = activeModeId;
+
+    mFrameRateOverrideConfig = [&] {
+        switch (mConfig.enableFrameRateOverride) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverride:
+            case Config::FrameRateOverride::Enabled:
+                return mConfig.enableFrameRateOverride;
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+                return shouldEnableFrameRateOverride(sortedModes)
+                        ? Config::FrameRateOverride::AppOverrideNativeRefreshRates
+                        : Config::FrameRateOverride::Disabled;
+        }
+    }();
+
+    if (mConfig.enableFrameRateOverride ==
+        Config::FrameRateOverride::AppOverrideNativeRefreshRates) {
+        for (const auto& [_, mode] : mDisplayModes) {
+            mAppOverrideNativeRefreshRates.try_emplace(mode->getFps(), ftl::unit);
+        }
+    }
+
+    constructAvailableRefreshRates();
+}
+
+bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const {
+    // defaultMode must be a valid mode, and within the given refresh rate range.
+    if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
+        if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) {
+            ALOGE("Default mode is not in the primary range.");
+            return false;
+        }
+    } else {
+        ALOGE("Default mode is not found.");
+        return false;
+    }
+
+    const auto& primaryRanges = policy.primaryRanges;
+    const auto& appRequestRanges = policy.appRequestRanges;
+    ALOGE_IF(!appRequestRanges.physical.includes(primaryRanges.physical),
+             "Physical range is invalid: primary: %s appRequest: %s",
+             to_string(primaryRanges.physical).c_str(),
+             to_string(appRequestRanges.physical).c_str());
+    ALOGE_IF(!appRequestRanges.render.includes(primaryRanges.render),
+             "Render range is invalid: primary: %s appRequest: %s",
+             to_string(primaryRanges.render).c_str(), to_string(appRequestRanges.render).c_str());
+
+    return primaryRanges.valid() && appRequestRanges.valid();
+}
+
+auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
+    Policy oldPolicy;
+    PhysicalDisplayId displayId;
+    {
+        std::lock_guard lock(mLock);
+        oldPolicy = *getCurrentPolicyLocked();
+
+        const bool valid = ftl::match(
+                policy,
+                [this](const auto& policy) {
+                    ftl::FakeGuard guard(mLock);
+                    if (!isPolicyValidLocked(policy)) {
+                        ALOGE("Invalid policy: %s", policy.toString().c_str());
+                        return false;
+                    }
+
+                    using T = std::decay_t<decltype(policy)>;
+
+                    if constexpr (std::is_same_v<T, DisplayManagerPolicy>) {
+                        mDisplayManagerPolicy = policy;
+                    } else {
+                        static_assert(std::is_same_v<T, OverridePolicy>);
+                        mOverridePolicy = policy;
+                    }
+                    return true;
+                },
+                [this](NoOverridePolicy) {
+                    ftl::FakeGuard guard(mLock);
+                    mOverridePolicy.reset();
+                    return true;
+                });
+
+        if (!valid) {
+            return SetPolicyResult::Invalid;
+        }
+
+        mGetRankedFrameRatesCache.reset();
+
+        if (*getCurrentPolicyLocked() == oldPolicy) {
+            return SetPolicyResult::Unchanged;
+        }
+        constructAvailableRefreshRates();
+
+        displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId();
+    }
+
+    const unsigned numModeChanges = std::exchange(mNumModeSwitchesInPolicy, 0u);
+
+    ALOGI("Display %s policy changed\n"
+          "Previous: %s\n"
+          "Current:  %s\n"
+          "%u mode changes were performed under the previous policy",
+          to_string(displayId).c_str(), oldPolicy.toString().c_str(), toString(policy).c_str(),
+          numModeChanges);
+
+    return SetPolicyResult::Changed;
+}
+
+auto RefreshRateSelector::getCurrentPolicyLocked() const -> const Policy* {
+    return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
+}
+
+auto RefreshRateSelector::getCurrentPolicy() const -> Policy {
+    std::lock_guard lock(mLock);
+    return *getCurrentPolicyLocked();
+}
+
+auto RefreshRateSelector::getDisplayManagerPolicy() const -> Policy {
+    std::lock_guard lock(mLock);
+    return mDisplayManagerPolicy;
+}
+
+bool RefreshRateSelector::isModeAllowed(const FrameRateMode& mode) const {
+    std::lock_guard lock(mLock);
+    return std::find(mAppRequestFrameRates.begin(), mAppRequestFrameRates.end(), mode) !=
+            mAppRequestFrameRates.end();
+}
+
+void RefreshRateSelector::constructAvailableRefreshRates() {
+    // Filter modes based on current policy and sort on refresh rate.
+    const Policy* policy = getCurrentPolicyLocked();
+    ALOGV("%s: %s ", __func__, policy->toString().c_str());
+
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
+
+    const auto filterRefreshRates = [&](const FpsRanges& ranges,
+                                        const char* rangeName) REQUIRES(mLock) {
+        const auto filterModes = [&](const DisplayMode& mode) {
+            return mode.getResolution() == defaultMode->getResolution() &&
+                    mode.getDpi() == defaultMode->getDpi() &&
+                    (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
+                    ranges.physical.includes(mode.getFps()) &&
+                    (supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
+        };
+
+        const auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+        LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
+                            "No matching frame rate modes for %s range. policy: %s", rangeName,
+                            policy->toString().c_str());
+
+        const auto stringifyModes = [&] {
+            std::string str;
+            for (const auto& frameRateMode : frameRateModes) {
+                str += to_string(frameRateMode) + " ";
+            }
+            return str;
+        };
+        ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
+
+        return frameRateModes;
+    };
+
+    mPrimaryFrameRates = filterRefreshRates(policy->primaryRanges, "primary");
+    mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
+}
+
+Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
+    using namespace fps_approx_ops;
+
+    if (frameRate <= mKnownFrameRates.front()) {
+        return mKnownFrameRates.front();
+    }
+
+    if (frameRate >= mKnownFrameRates.back()) {
+        return mKnownFrameRates.back();
+    }
+
+    auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
+                                       isStrictlyLess);
+
+    const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
+    const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
+    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+auto RefreshRateSelector::getIdleTimerAction() const -> KernelIdleTimerAction {
+    std::lock_guard lock(mLock);
+
+    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
+    const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
+
+    // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+    // the min allowed refresh rate is higher than the device min, we do not want to enable the
+    // timer.
+    if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
+        return KernelIdleTimerAction::TurnOff;
+    }
+
+    const DisplayModePtr& maxByPolicy =
+            getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup());
+    if (minByPolicy == maxByPolicy) {
+        // Turn on the timer when the min of the primary range is below the device min.
+        if (const Policy* currentPolicy = getCurrentPolicyLocked();
+            isApproxLess(currentPolicy->primaryRanges.physical.min, deviceMinFps)) {
+            return KernelIdleTimerAction::TurnOn;
+        }
+        return KernelIdleTimerAction::TurnOff;
+    }
+
+    // Turn on the timer in all other cases.
+    return KernelIdleTimerAction::TurnOn;
+}
+
+int RefreshRateSelector::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
+    // This calculation needs to be in sync with the java code
+    // in DisplayManagerService.getDisplayInfoForFrameRateOverride
+
+    // The threshold must be smaller than 0.001 in order to differentiate
+    // between the fractional pairs (e.g. 59.94 and 60).
+    constexpr float kThreshold = 0.0009f;
+    const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
+    const auto numPeriodsRounded = std::round(numPeriods);
+    if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+        return 0;
+    }
+
+    return static_cast<int>(numPeriodsRounded);
+}
+
+bool RefreshRateSelector::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+    if (isStrictlyLess(bigger, smaller)) {
+        return isFractionalPairOrMultiple(bigger, smaller);
+    }
+
+    const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+    constexpr float kCoef = 1000.f / 1001.f;
+    return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
+            isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
+}
+
+void RefreshRateSelector::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
+
+    std::lock_guard lock(mLock);
+
+    const auto activeMode = getActiveModeLocked();
+    dumper.dump("activeMode"sv, to_string(activeMode));
+
+    dumper.dump("displayModes"sv);
+    {
+        utils::Dumper::Indent indent(dumper);
+        for (const auto& [id, mode] : mDisplayModes) {
+            dumper.dump({}, to_string(*mode));
+        }
+    }
+
+    dumper.dump("displayManagerPolicy"sv, mDisplayManagerPolicy.toString());
+
+    if (const Policy& currentPolicy = *getCurrentPolicyLocked();
+        mOverridePolicy && currentPolicy != mDisplayManagerPolicy) {
+        dumper.dump("overridePolicy"sv, currentPolicy.toString());
+    }
+
+    dumper.dump("frameRateOverrideConfig"sv, *ftl::enum_name(mFrameRateOverrideConfig));
+
+    dumper.dump("idleTimer"sv);
+    {
+        utils::Dumper::Indent indent(dumper);
+        dumper.dump("interval"sv, mIdleTimer.transform(&OneShotTimer::interval));
+        dumper.dump("controller"sv,
+                    mConfig.kernelIdleTimerController
+                            .and_then(&ftl::enum_name<KernelIdleTimerController>)
+                            .value_or("Platform"sv));
+    }
+}
+
+std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() {
+    return mConfig.idleTimerTimeout;
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
new file mode 100644
index 0000000..5052e6e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -0,0 +1,518 @@
+/*
+ * 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 <algorithm>
+#include <numeric>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include <ftl/concat.h>
+#include <ftl/optional.h>
+#include <ftl/unit.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
+#include <scheduler/Seamlessness.h>
+
+#include "DisplayHardware/DisplayMode.h"
+#include "Scheduler/OneShotTimer.h"
+#include "Scheduler/StrongTyping.h"
+#include "ThreadContext.h"
+#include "Utils/Dumper.h"
+
+namespace android::scheduler {
+
+using namespace std::chrono_literals;
+
+enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
+
+inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
+    using T = std::underlying_type_t<DisplayModeEvent>;
+    return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+}
+
+using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+
+// Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with
+// the DisplayManager (or override) `Policy`, the `LayerRequirement` of each active layer,
+// and `GlobalSignals`.
+class RefreshRateSelector {
+public:
+    // Margin used when matching refresh rates to the content desired ones.
+    static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
+            std::chrono::nanoseconds(800us).count();
+
+    // The lowest Render Frame Rate that will ever be selected
+    static constexpr Fps kMinSupportedFrameRate = 20_Hz;
+
+    class Policy {
+        static constexpr int kAllowGroupSwitchingDefault = false;
+
+    public:
+        // The default mode, used to ensure we only initiate display mode switches within the
+        // same mode group as defaultMode's group.
+        DisplayModeId defaultMode;
+        // Whether or not we switch mode groups to get the best frame rate.
+        bool allowGroupSwitching = kAllowGroupSwitchingDefault;
+        // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+        // TODO(b/257072060): use the render range when selecting SF render rate
+        //  or the app override frame rate
+        FpsRanges primaryRanges;
+        // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+        FpsRanges appRequestRanges;
+
+        Policy() = default;
+
+        Policy(DisplayModeId defaultMode, FpsRange range,
+               bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+              : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range},
+                       allowGroupSwitching) {}
+
+        Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges,
+               bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+              : defaultMode(defaultMode),
+                allowGroupSwitching(allowGroupSwitching),
+                primaryRanges(primaryRanges),
+                appRequestRanges(appRequestRanges) {}
+
+        bool operator==(const Policy& other) const {
+            using namespace fps_approx_ops;
+            return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges &&
+                    appRequestRanges == other.appRequestRanges &&
+                    allowGroupSwitching == other.allowGroupSwitching;
+        }
+
+        bool operator!=(const Policy& other) const { return !(*this == other); }
+        std::string toString() const;
+    };
+
+    enum class SetPolicyResult { Invalid, Unchanged, Changed };
+
+    // We maintain the display manager policy and the override policy separately. The override
+    // policy is used by CTS tests to get a consistent device state for testing. While the override
+    // policy is set, it takes precedence over the display manager policy. Once the override policy
+    // is cleared, we revert to using the display manager policy.
+    struct DisplayManagerPolicy : Policy {
+        using Policy::Policy;
+    };
+
+    struct OverridePolicy : Policy {
+        using Policy::Policy;
+    };
+
+    struct NoOverridePolicy {};
+
+    using PolicyVariant = std::variant<DisplayManagerPolicy, OverridePolicy, NoOverridePolicy>;
+
+    SetPolicyResult setPolicy(const PolicyVariant&) EXCLUDES(mLock) REQUIRES(kMainThreadContext);
+
+    void onModeChangeInitiated() REQUIRES(kMainThreadContext) { mNumModeSwitchesInPolicy++; }
+
+    // Gets the current policy, which will be the override policy if active, and the display manager
+    // policy otherwise.
+    Policy getCurrentPolicy() const EXCLUDES(mLock);
+    // Gets the display manager policy, regardless of whether an override policy is active.
+    Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
+
+    // Returns true if mode is allowed by the current policy.
+    bool isModeAllowed(const FrameRateMode&) const EXCLUDES(mLock);
+
+    // Describes the different options the layer voted for refresh rate
+    enum class LayerVoteType {
+        NoVote,          // Doesn't care about the refresh rate
+        Min,             // Minimal refresh rate available
+        Max,             // Maximal refresh rate available
+        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
+        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+                         // compatibility
+        ExplicitExactOrMultiple, // Specific refresh rate that was provided by the app with
+                                 // ExactOrMultiple compatibility
+        ExplicitExact,           // Specific refresh rate that was provided by the app with
+                                 // Exact compatibility
+
+        ftl_last = ExplicitExact
+    };
+
+    // Captures the layer requirements for a refresh rate. This will be used to determine the
+    // display refresh rate.
+    struct LayerRequirement {
+        // Layer's name. Used for debugging purposes.
+        std::string name;
+        // Layer's owner uid
+        uid_t ownerUid = static_cast<uid_t>(-1);
+        // Layer vote type.
+        LayerVoteType vote = LayerVoteType::NoVote;
+        // Layer's desired refresh rate, if applicable.
+        Fps desiredRefreshRate;
+        // If a seamless mode switch is required.
+        Seamlessness seamlessness = Seamlessness::Default;
+        // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
+        // would have on choosing the refresh rate.
+        float weight = 0.0f;
+        // Whether layer is in focus or not based on WindowManager's state
+        bool focused = false;
+
+        bool operator==(const LayerRequirement& other) const {
+            return name == other.name && vote == other.vote &&
+                    isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
+                    seamlessness == other.seamlessness && weight == other.weight &&
+                    focused == other.focused;
+        }
+
+        bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+    };
+
+    // Global state describing signals that affect refresh rate choice.
+    struct GlobalSignals {
+        // Whether the user touched the screen recently. Used to apply touch boost.
+        bool touch = false;
+        // True if the system hasn't seen any buffers posted to layers recently.
+        bool idle = false;
+        // Whether the display is about to be powered on, or has been in PowerMode::ON
+        // within the timeout of DisplayPowerTimer.
+        bool powerOnImminent = false;
+
+        bool operator==(GlobalSignals other) const {
+            return touch == other.touch && idle == other.idle &&
+                    powerOnImminent == other.powerOnImminent;
+        }
+
+        auto toString() const {
+            return ftl::Concat("{touch=", touch, ", idle=", idle,
+                               ", powerOnImminent=", powerOnImminent, '}');
+        }
+    };
+
+    struct ScoredFrameRate {
+        FrameRateMode frameRateMode;
+        float score = 0.0f;
+
+        bool operator==(const ScoredFrameRate& other) const {
+            return frameRateMode == other.frameRateMode && score == other.score;
+        }
+
+        static bool scoresEqual(float lhs, float rhs) {
+            constexpr float kEpsilon = 0.0001f;
+            return std::abs(lhs - rhs) <= kEpsilon;
+        }
+
+        struct DescendingScore {
+            bool operator()(const ScoredFrameRate& lhs, const ScoredFrameRate& rhs) const {
+                return lhs.score > rhs.score && !scoresEqual(lhs.score, rhs.score);
+            }
+        };
+    };
+
+    using FrameRateRanking = std::vector<ScoredFrameRate>;
+
+    struct RankedFrameRates {
+        FrameRateRanking ranking; // Ordered by descending score.
+        GlobalSignals consideredSignals;
+
+        bool operator==(const RankedFrameRates& other) const {
+            return ranking == other.ranking && consideredSignals == other.consideredSignals;
+        }
+    };
+
+    RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
+            EXCLUDES(mLock);
+
+    FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
+    }
+
+    ftl::Optional<FrameRateMode> onKernelTimerChanged(
+            std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+            EXCLUDES(mLock);
+
+    void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
+
+    // See mActiveModeOpt for thread safety.
+    FrameRateMode getActiveMode() const EXCLUDES(mLock);
+
+    // Returns a known frame rate that is the closest to frameRate
+    Fps findClosestKnownFrameRate(Fps frameRate) const;
+
+    enum class KernelIdleTimerController { Sysprop, HwcApi, ftl_last = HwcApi };
+
+    // Configuration flags.
+    struct Config {
+        enum class FrameRateOverride {
+            // Do not override the frame rate for an app
+            Disabled,
+
+            // Override the frame rate for an app to a value which is also
+            // a display refresh rate
+            AppOverrideNativeRefreshRates,
+
+            // Override the frame rate for an app to any value
+            AppOverride,
+
+            // Override the frame rate for all apps and all values.
+            Enabled,
+
+            ftl_last = Enabled
+        };
+        FrameRateOverride enableFrameRateOverride = FrameRateOverride::Disabled;
+
+        // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
+        // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
+        // no threshold is set.
+        int frameRateMultipleThreshold = 0;
+
+        // The Idle Timer timeout. 0 timeout means no idle timer.
+        std::chrono::milliseconds idleTimerTimeout = 0ms;
+
+        // The controller representing how the kernel idle timer will be configured
+        // either on the HWC api or sysprop.
+        ftl::Optional<KernelIdleTimerController> kernelIdleTimerController;
+    };
+
+    RefreshRateSelector(
+            DisplayModes, DisplayModeId activeModeId,
+            Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
+                             .frameRateMultipleThreshold = 0,
+                             .idleTimerTimeout = 0ms,
+                             .kernelIdleTimerController = {}});
+
+    RefreshRateSelector(const RefreshRateSelector&) = delete;
+    RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
+
+    const DisplayModes& displayModes() const { return mDisplayModes; }
+
+    // Returns whether switching modes (refresh rate or resolution) is possible.
+    // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
+    //  differ in resolution. Once Config::FrameRateOverride::Enabled becomes the default,
+    //  we can probably remove canSwitch altogether since all devices will be able
+    //  to switch to a frame rate divisor.
+    bool canSwitch() const EXCLUDES(mLock) {
+        std::lock_guard lock(mLock);
+        return mDisplayModes.size() > 1 ||
+                mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+    }
+
+    // Class to enumerate options around toggling the kernel timer on and off.
+    enum class KernelIdleTimerAction {
+        TurnOff, // Turn off the idle timer.
+        TurnOn   // Turn on the idle timer.
+    };
+
+    // Checks whether kernel idle timer should be active depending the policy decisions around
+    // refresh rates.
+    KernelIdleTimerAction getIdleTimerAction() const;
+
+    bool supportsAppFrameRateOverrideByContent() const {
+        return mFrameRateOverrideConfig != Config::FrameRateOverride::Disabled;
+    }
+
+    bool supportsFrameRateOverride() const {
+        return mFrameRateOverrideConfig == Config::FrameRateOverride::Enabled;
+    }
+
+    // Return the display refresh rate divisor to match the layer
+    // frame rate, or 0 if the display refresh rate is not a multiple of the
+    // layer refresh rate.
+    static int getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate);
+
+    // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+    // for an integer t.
+    static bool isFractionalPairOrMultiple(Fps, Fps);
+
+    using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
+    // Returns the frame rate override for each uid.
+    UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
+                                                 Fps displayFrameRate, GlobalSignals) const
+            EXCLUDES(mLock);
+
+    std::optional<KernelIdleTimerController> kernelIdleTimerController() {
+        return mConfig.kernelIdleTimerController;
+    }
+
+    struct IdleTimerCallbacks {
+        struct Callbacks {
+            std::function<void()> onReset;
+            std::function<void()> onExpired;
+        };
+
+        Callbacks platform;
+        Callbacks kernel;
+    };
+
+    void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
+        std::scoped_lock lock(mIdleTimerCallbacksMutex);
+        mIdleTimerCallbacks = std::move(callbacks);
+    }
+
+    void clearIdleTimerCallbacks() EXCLUDES(mIdleTimerCallbacksMutex) {
+        std::scoped_lock lock(mIdleTimerCallbacksMutex);
+        mIdleTimerCallbacks.reset();
+    }
+
+    void startIdleTimer() {
+        if (mIdleTimer) {
+            mIdleTimer->start();
+        }
+    }
+
+    void stopIdleTimer() {
+        if (mIdleTimer) {
+            mIdleTimer->stop();
+        }
+    }
+
+    void resetKernelIdleTimer() {
+        if (mIdleTimer && mConfig.kernelIdleTimerController) {
+            mIdleTimer->reset();
+        }
+    }
+
+    void resetIdleTimer() {
+        if (mIdleTimer) {
+            mIdleTimer->reset();
+        }
+    }
+
+    void dump(utils::Dumper&) const EXCLUDES(mLock);
+
+    std::chrono::milliseconds getIdleTimerTimeout();
+
+private:
+    friend struct TestableRefreshRateSelector;
+
+    void constructAvailableRefreshRates() REQUIRES(mLock);
+
+    // See mActiveModeOpt for thread safety.
+    const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
+
+    RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
+                                               GlobalSignals signals) const REQUIRES(mLock);
+
+    // Returns number of display frames and remainder when dividing the layer refresh period by
+    // display refresh period.
+    std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
+
+    // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
+    const DisplayModePtr& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
+    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
+    const DisplayModePtr& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
+
+    struct RefreshRateScoreComparator;
+
+    enum class RefreshRateOrder {
+        Ascending,
+        Descending,
+
+        ftl_last = Descending
+    };
+
+    // Only uses the primary range, not the app request range.
+    FrameRateRanking rankFrameRates(
+            std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+            std::optional<DisplayModeId> preferredDisplayModeOpt = std::nullopt) const
+            REQUIRES(mLock);
+
+    const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
+    bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
+
+    // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1.
+    float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock);
+    // calculates a score for a layer. Used to determine the display refresh rate
+    // and the frame rate override for certains applications.
+    float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate,
+                                    bool isSeamlessSwitch) const REQUIRES(mLock);
+
+    float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
+            REQUIRES(mLock);
+
+    void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
+            REQUIRES(kMainThreadContext);
+
+    void initializeIdleTimer();
+
+    std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
+            REQUIRES(mIdleTimerCallbacksMutex) {
+        if (!mIdleTimerCallbacks) return {};
+        return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
+                                                             : mIdleTimerCallbacks->platform;
+    }
+
+    bool isNativeRefreshRate(Fps fps) const REQUIRES(mLock) {
+        LOG_ALWAYS_FATAL_IF(mConfig.enableFrameRateOverride !=
+                                    Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+                            "should only be called when "
+                            "Config::FrameRateOverride::AppOverrideNativeRefreshRates is used");
+        return mAppOverrideNativeRefreshRates.contains(fps);
+    }
+
+    std::vector<FrameRateMode> createFrameRateModes(
+            std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
+            REQUIRES(mLock);
+
+    // The display modes of the active display. The DisplayModeIterators below are pointers into
+    // this container, so must be invalidated whenever the DisplayModes change. The Policy below
+    // is also dependent, so must be reset as well.
+    DisplayModes mDisplayModes GUARDED_BY(mLock);
+
+    // Set of supported display refresh rates for easy lookup
+    // when FrameRateOverride::AppOverrideNativeRefreshRates is in use.
+    ftl::SmallMap<Fps, ftl::Unit, 8, FpsApproxEqual> mAppOverrideNativeRefreshRates;
+
+    ftl::Optional<FrameRateMode> mActiveModeOpt GUARDED_BY(mLock);
+
+    DisplayModeIterator mMinRefreshRateModeIt GUARDED_BY(mLock);
+    DisplayModeIterator mMaxRefreshRateModeIt GUARDED_BY(mLock);
+
+    // 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);
+
+    Policy mDisplayManagerPolicy GUARDED_BY(mLock);
+    std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+
+    unsigned mNumModeSwitchesInPolicy GUARDED_BY(kMainThreadContext) = 0;
+
+    mutable std::mutex mLock;
+
+    // A sorted list of known frame rates that a Heuristic layer will choose
+    // from based on the closest value.
+    const std::vector<Fps> mKnownFrameRates;
+
+    const Config mConfig;
+    Config::FrameRateOverride mFrameRateOverrideConfig;
+
+    struct GetRankedFrameRatesCache {
+        std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+        RankedFrameRates result;
+    };
+    mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
+
+    // Declare mIdleTimer last to ensure its thread joins before the mutex/callbacks are destroyed.
+    std::mutex mIdleTimerCallbacksMutex;
+    std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
+    // Used to detect (lack of) frame activity.
+    ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index ed65bc6..67e1b9c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include <android-base/stringprintf.h>
+#include <ftl/algorithm.h>
 #include <ftl/small_map.h>
 #include <utils/Timers.h>
 
@@ -82,12 +83,18 @@
         flushTime();
 
         TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
-        const auto zero = std::chrono::milliseconds::zero();
 
         // Sum the times for modes that map to the same name, e.g. "60 Hz".
         for (const auto& [fps, time] : mFpsTotalTimes) {
             const auto string = to_string(fps);
-            const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+            const auto total = std::as_const(totalTimes)
+                                       .get(string)
+                                       .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+                                           using namespace std::chrono_literals;
+                                           return 0ms;
+                                       }))
+                                       .value();
+
             totalTimes.emplace_or_replace(string, total.get() + time);
         }
 
@@ -114,15 +121,18 @@
         mPreviousRecordedTime = currentTime;
 
         const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
-        const auto zero = std::chrono::milliseconds::zero();
-
         uint32_t fps = 0;
 
         if (mCurrentPowerMode == PowerMode::ON) {
             // Normal power mode is counted under different config modes.
             const auto total = std::as_const(mFpsTotalTimes)
                                        .get(mCurrentRefreshRate)
-                                       .value_or(std::cref(zero));
+                                       .or_else(ftl::static_ref<std::chrono::milliseconds>([] {
+                                           using namespace std::chrono_literals;
+                                           return 0ms;
+                                       }))
+                                       .value();
+
             mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
 
             fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8b1a5d9..9319543 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -25,14 +25,17 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
+#include <ftl/enum.h>
 #include <ftl/fake_guard.h>
+#include <ftl/small_map.h>
+#include <gui/TraceUtils.h>
 #include <gui/WindowInfo.h>
 #include <system/window.h>
-#include <ui/DisplayStatInfo.h>
 #include <utils/Timers.h>
-#include <utils/Trace.h>
 
 #include <FrameTimeline/FrameTimeline.h>
+#include <scheduler/interface/ICompositor.h>
+
 #include <algorithm>
 #include <cinttypes>
 #include <cstdint>
@@ -41,14 +44,15 @@
 #include <numeric>
 
 #include "../Layer.h"
-#include "DispSyncSource.h"
+#include "Display/DisplayMap.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
-#include "InjectVSyncSource.h"
+#include "FrontEnd/LayerHandle.h"
 #include "OneShotTimer.h"
 #include "SurfaceFlingerProperties.h"
-#include "VSyncPredictor.h"
-#include "VSyncReactor.h"
+#include "VSyncTracker.h"
+#include "VsyncController.h"
+#include "VsyncSchedule.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -60,16 +64,25 @@
 
 namespace android::scheduler {
 
-Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
-      : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
+                     sp<VsyncModulator> modulatorPtr)
+      : impl::MessageQueue(compositor),
+        mFeatures(features),
+        mVsyncModulator(std::move(modulatorPtr)),
+        mSchedulerCallback(callback) {}
 
 Scheduler::~Scheduler() {
+    // MessageQueue depends on VsyncSchedule, so first destroy it.
+    // Otherwise, MessageQueue will get destroyed after Scheduler's dtor,
+    // which will cause a use-after-free issue.
+    Impl::destroyVsync();
+
     // Stop timers and wait for their threads to exit.
     mDisplayPowerTimer.reset();
     mTouchTimer.reset();
 
-    // Stop idle timer and clear callbacks, as the RefreshRateConfigs may outlive the Scheduler.
-    setRefreshRateConfigs(nullptr);
+    // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
+    demotePacesetterDisplay();
 }
 
 void Scheduler::startTimers() {
@@ -94,36 +107,48 @@
     }
 }
 
-void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) {
-    // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer.
+void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    demotePacesetterDisplay();
+
+    promotePacesetterDisplay(pacesetterIdOpt);
+}
+
+void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+    registerDisplayInternal(displayId, std::move(selectorPtr),
+                            std::make_shared<VsyncSchedule>(displayId, mFeatures));
+}
+
+void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
+                                        RefreshRateSelectorPtr selectorPtr,
+                                        VsyncSchedulePtr schedulePtr) {
+    demotePacesetterDisplay();
+
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
-        // mRefreshRateConfigsLock is not locked here to avoid the deadlock
-        // as the callback can attempt to acquire the lock before stopIdleTimer can finish
-        // the execution. It's safe to FakeGuard as main thread is the only thread that
-        // writes to the mRefreshRateConfigs.
-        ftl::FakeGuard guard(mRefreshRateConfigsLock);
-        if (mRefreshRateConfigs) {
-            mRefreshRateConfigs->stopIdleTimer();
-            mRefreshRateConfigs->clearIdleTimerCallbacks();
-        }
+        std::scoped_lock lock(mDisplayLock);
+        mDisplays.emplace_or_replace(displayId, std::move(selectorPtr), std::move(schedulePtr));
+
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
     }
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
+}
+
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+    demotePacesetterDisplay();
+
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
     {
-        // Clear state that depends on the current instance.
-        std::scoped_lock lock(mPolicyLock);
-        mPolicy = {};
+        std::scoped_lock lock(mDisplayLock);
+        mDisplays.erase(displayId);
+
+        // Do not allow removing the final display. Code in the scheduler expects
+        // there to be at least one display. (This may be relaxed in the future with
+        // headless virtual display.)
+        LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
+
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
     }
-
-    std::scoped_lock lock(mRefreshRateConfigsLock);
-    mRefreshRateConfigs = std::move(configs);
-    if (!mRefreshRateConfigs) return;
-
-    mRefreshRateConfigs->setIdleTimerCallbacks(
-            {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
-                          .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
-             .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
-                        .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
-
-    mRefreshRateConfigs->startIdleTimer();
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
 }
 
 void Scheduler::run() {
@@ -132,74 +157,84 @@
     }
 }
 
-void Scheduler::createVsyncSchedule(FeatureFlags features) {
-    mVsyncSchedule.emplace(features);
-}
+void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId,
+                              TimePoint expectedVsyncTime) {
+    const TimePoint frameTime = SchedulerClock::now();
 
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
-        const char* name, std::chrono::nanoseconds workDuration,
-        std::chrono::nanoseconds readyDuration, bool traceVsync) {
-    return std::make_unique<scheduler::DispSyncSource>(mVsyncSchedule->getDispatch(),
-                                                       mVsyncSchedule->getTracker(), workDuration,
-                                                       readyDuration, traceVsync, name);
+    if (!compositor.commit(frameTime, vsyncId, expectedVsyncTime)) {
+        return;
+    }
+
+    compositor.composite(frameTime, vsyncId);
+    compositor.sample();
 }
 
 std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     const bool supportsFrameRateOverrideByContent =
-            refreshRateConfigs->supportsFrameRateOverrideByContent();
+            pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
     return mFrameRateOverrideMappings
             .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent);
 }
 
-bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const {
     const auto frameRate = getFrameRateOverride(uid);
     if (!frameRate.has_value()) {
         return true;
     }
 
-    return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+    ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str());
+    return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate);
+}
+
+bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const {
+    return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate);
 }
 
 impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
-    std::scoped_lock lock(mRefreshRateConfigsLock);
-
     return [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
-        return !isVsyncValid(expectedVsyncTimestamp, uid);
+        return !isVsyncValid(TimePoint::fromNs(expectedVsyncTimestamp), uid);
     };
 }
 
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
-        const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
-        const auto currentPeriod =
-                mVsyncSchedule->getTracker().currentPeriod() ?: refreshRate.getPeriodNsecs();
+        const auto [refreshRate, period] = [this] {
+            std::scoped_lock lock(mDisplayLock);
+            const auto pacesetterOpt = pacesetterDisplayLocked();
+            LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+            const Display& pacesetter = *pacesetterOpt;
+            return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
+                                  pacesetter.schedulePtr->period());
+        }();
+
+        const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
 
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
-            return currentPeriod;
+            return currentPeriod.ns();
         }
 
-        const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate);
+        const auto divisor = RefreshRateSelector::getFrameRateDivisor(refreshRate, *frameRate);
         if (divisor <= 1) {
-            return currentPeriod;
+            return currentPeriod.ns();
         }
-        return currentPeriod * divisor;
+        return currentPeriod.ns() * divisor;
     };
 }
 
-ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, frametimeline::TokenManager* tokenManager,
-        std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
-        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
-    auto throttleVsync = makeThrottleVsyncCallback();
-    auto getVsyncPeriod = makeGetVsyncPeriodFunction();
-    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
-                                                           std::move(interceptCallback),
-                                                           std::move(throttleVsync),
-                                                           std::move(getVsyncPeriod));
-    return createConnection(std::move(eventThread));
+ConnectionHandle Scheduler::createEventThread(Cycle cycle,
+                                              frametimeline::TokenManager* tokenManager,
+                                              std::chrono::nanoseconds workDuration,
+                                              std::chrono::nanoseconds readyDuration) {
+    auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
+                                                           getVsyncSchedule(), tokenManager,
+                                                           makeThrottleVsyncCallback(),
+                                                           makeGetVsyncPeriodFunction(),
+                                                           workDuration, readyDuration);
+
+    auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
+    handle = createConnection(std::move(eventThread));
+    return handle;
 }
 
 ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -214,15 +249,21 @@
 }
 
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
-    return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
+        EventThread* eventThread, EventRegistrationFlags eventRegistration,
+        const sp<IBinder>& layerHandle) {
+    int32_t layerId = static_cast<int32_t>(LayerHandle::getLayerId(layerHandle));
+    auto connection = eventThread->createEventConnection([&] { resync(); }, eventRegistration);
+    mLayerHistory.attachChoreographer(layerId, connection);
+    return connection;
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        ConnectionHandle handle, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+        ConnectionHandle handle, EventRegistrationFlags eventRegistration,
+        const sp<IBinder>& layerHandle) {
     std::lock_guard<std::mutex> lock(mConnectionsLock);
     RETURN_IF_INVALID_HANDLE(handle, nullptr);
-    return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration);
+    return createConnectionInternal(mConnections[handle].thread.get(), eventRegistration,
+                                    layerHandle);
 }
 
 sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
@@ -243,32 +284,21 @@
     thread->onHotplugReceived(displayId, connected);
 }
 
-void Scheduler::onScreenAcquired(ConnectionHandle handle) {
+void Scheduler::enableSyntheticVsync(bool enable) {
+    // TODO(b/241285945): Remove connection handles.
+    const ConnectionHandle handle = mAppConnectionHandle;
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
         RETURN_IF_INVALID_HANDLE(handle);
         thread = mConnections[handle].thread.get();
     }
-    thread->onScreenAcquired();
-    mScreenAcquired = true;
-}
-
-void Scheduler::onScreenReleased(ConnectionHandle handle) {
-    android::EventThread* thread;
-    {
-        std::lock_guard<std::mutex> lock(mConnectionsLock);
-        RETURN_IF_INVALID_HANDLE(handle);
-        thread = mConnections[handle].thread.get();
-    }
-    thread->onScreenReleased();
-    mScreenAcquired = false;
+    thread->enableSyntheticVsync(enable);
 }
 
 void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     const bool supportsFrameRateOverrideByContent =
-            refreshRateConfigs->supportsFrameRateOverrideByContent();
+            pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
 
     std::vector<FrameRateOverride> overrides =
             mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
@@ -282,7 +312,7 @@
     thread->onFrameRateOverridesChanged(displayId, std::move(overrides));
 }
 
-void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
     {
         std::lock_guard<std::mutex> lock(mPolicyLock);
         // Cache the last reported modes for primary display.
@@ -297,7 +327,7 @@
 
 void Scheduler::dispatchCachedReportedMode() {
     // Check optional fields first.
-    if (!mPolicy.mode) {
+    if (!mPolicy.modeOpt) {
         ALOGW("No mode ID found, not dispatching cached mode.");
         return;
     }
@@ -309,22 +339,21 @@
     // If the mode is not the current mode, this means that a
     // mode change is in progress. In that case we shouldn't dispatch an event
     // as it will be dispatched when the current mode changes.
-    if (std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->getActiveMode() != mPolicy.mode) {
+    if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
         return;
     }
 
     // If there is no change from cached mode, there is no need to dispatch an event
-    if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
+    if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
         return;
     }
 
-    mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+    mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
     onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
                                    mPolicy.cachedModeChangedParams->mode);
 }
 
-void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
     android::EventThread* thread;
     {
         std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -361,83 +390,84 @@
     thread->setDuration(workDuration, readyDuration);
 }
 
-DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
-    const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
-    const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
-    return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
+void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) {
+    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod);
 }
 
-ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
-    if (mInjectVSyncs == enable) {
-        return {};
-    }
-
-    ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
-
-    if (!mInjectorConnectionHandle) {
-        auto vsyncSource = std::make_unique<InjectVSyncSource>();
-        mVSyncInjector = vsyncSource.get();
-
-        auto eventThread =
-                std::make_unique<impl::EventThread>(std::move(vsyncSource),
-                                                    /*tokenManager=*/nullptr,
-                                                    impl::EventThread::InterceptVSyncsCallback(),
-                                                    impl::EventThread::ThrottleVsyncCallback(),
-                                                    impl::EventThread::GetVsyncPeriodFunction());
-
-        // EventThread does not dispatch VSYNC unless the display is connected and powered on.
-        eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
-        eventThread->onScreenAcquired();
-
-        mInjectorConnectionHandle = createConnection(std::move(eventThread));
-    }
-
-    mInjectVSyncs = enable;
-    return mInjectorConnectionHandle;
+void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
+    setDuration(mAppConnectionHandle,
+                /* workDuration */ config.appWorkDuration,
+                /* readyDuration */ config.sfWorkDuration);
+    setDuration(mSfConnectionHandle,
+                /* workDuration */ vsyncPeriod,
+                /* readyDuration */ config.sfWorkDuration);
+    setDuration(config.sfWorkDuration);
 }
 
-bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) {
-    if (!mInjectVSyncs || !mVSyncInjector) {
-        return false;
-    }
-
-    mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp);
-    return true;
+void Scheduler::enableHardwareVsync(PhysicalDisplayId id) {
+    auto schedule = getVsyncSchedule(id);
+    LOG_ALWAYS_FATAL_IF(!schedule);
+    schedule->enableHardwareVsync(mSchedulerCallback);
 }
 
-void Scheduler::enableHardwareVsync() {
-    std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
-        mVsyncSchedule->getTracker().resetModel();
-        mSchedulerCallback.setVsyncEnabled(true);
-        mPrimaryHWVsyncEnabled = true;
+void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) {
+    auto schedule = getVsyncSchedule(id);
+    LOG_ALWAYS_FATAL_IF(!schedule);
+    schedule->disableHardwareVsync(mSchedulerCallback, disallow);
+}
+
+void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) {
+    ATRACE_CALL();
+    std::scoped_lock lock(mDisplayLock);
+    ftl::FakeGuard guard(kMainThreadContext);
+
+    for (const auto& [id, _] : mDisplays) {
+        resyncToHardwareVsyncLocked(id, allowToEnable);
     }
 }
 
-void Scheduler::disableHardwareVsync(bool makeUnavailable) {
-    std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    if (mPrimaryHWVsyncEnabled) {
-        mSchedulerCallback.setVsyncEnabled(false);
-        mPrimaryHWVsyncEnabled = false;
+void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
+                                            std::optional<Fps> refreshRate) {
+    const auto displayOpt = mDisplays.get(id);
+    if (!displayOpt) {
+        ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+        return;
     }
-    if (makeUnavailable) {
-        mHWVsyncAvailable = false;
-    }
-}
+    const Display& display = *displayOpt;
 
-void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) {
-    {
-        std::lock_guard<std::mutex> lock(mHWVsyncLock);
-        if (makeAvailable) {
-            mHWVsyncAvailable = makeAvailable;
-        } else if (!mHWVsyncAvailable) {
-            // Hardware vsync is not currently available, so abort the resync
-            // attempt for now
-            return;
+    if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
+        if (!refreshRate) {
+            refreshRate = display.selectorPtr->getActiveMode().modePtr->getFps();
+        }
+        if (refreshRate->isValid()) {
+            display.schedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(),
+                                                       false /* force */);
         }
     }
+}
 
-    setVsyncPeriod(refreshRate.getPeriodNsecs());
+void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) {
+    std::scoped_lock lock(mDisplayLock);
+    ftl::FakeGuard guard(kMainThreadContext);
+
+    const auto displayOpt = mDisplays.get(id);
+    if (!displayOpt) {
+        ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+        return;
+    }
+    const Display& display = *displayOpt;
+    const auto mode = display.selectorPtr->getActiveMode();
+
+    using fps_approx_ops::operator!=;
+    LOG_ALWAYS_FATAL_IF(renderFrameRate != mode.fps,
+                        "Mismatch in render frame rates. Selector: %s, Scheduler: %s, Display: "
+                        "%" PRIu64,
+                        to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str(), id.value);
+
+    ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
+          to_string(mode.modePtr->getFps()).c_str());
+
+    display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
 }
 
 void Scheduler::resync() {
@@ -447,124 +477,100 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        const auto refreshRate = [&] {
-            std::scoped_lock lock(mRefreshRateConfigsLock);
-            return mRefreshRateConfigs->getActiveMode()->getFps();
-        }();
-        resyncToHardwareVsync(false, refreshRate);
+        resyncAllToHardwareVsync(false /* allowToEnable */);
     }
 }
 
-void Scheduler::setVsyncPeriod(nsecs_t period) {
-    if (period <= 0) return;
-
-    std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    mVsyncSchedule->getController().startPeriodTransition(period);
-
-    if (!mPrimaryHWVsyncEnabled) {
-        mVsyncSchedule->getTracker().resetModel();
-        mSchedulerCallback.setVsyncEnabled(true);
-        mPrimaryHWVsyncEnabled = true;
+bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp,
+                                std::optional<nsecs_t> hwcVsyncPeriodIn) {
+    const auto hwcVsyncPeriod = ftl::Optional(hwcVsyncPeriodIn).transform([](nsecs_t nanos) {
+        return Period::fromNs(nanos);
+    });
+    auto schedule = getVsyncSchedule(id);
+    if (!schedule) {
+        ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
+        return false;
     }
+    return schedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp),
+                                     hwcVsyncPeriod);
 }
 
-void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                                bool* periodFlushed) {
-    bool needsHwVsync = false;
-    *periodFlushed = false;
-    { // Scope for the lock
-        std::lock_guard<std::mutex> lock(mHWVsyncLock);
-        if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync =
-                    mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
-                                                                        periodFlushed);
-        }
-    }
-
-    if (needsHwVsync) {
-        enableHardwareVsync();
+void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) {
+    auto schedule = getVsyncSchedule(id);
+    LOG_ALWAYS_FATAL_IF(!schedule);
+    const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence));
+    if (needMoreSignals) {
+        schedule->enableHardwareVsync(mSchedulerCallback);
     } else {
-        disableHardwareVsync(false);
-    }
-}
-
-void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
-    if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
-        enableHardwareVsync();
-    } else {
-        disableHardwareVsync(false);
+        schedule->disableHardwareVsync(mSchedulerCallback, false /* disallow */);
     }
 }
 
 void Scheduler::registerLayer(Layer* layer) {
-    using WindowType = gui::WindowInfo::Type;
-
-    scheduler::LayerHistory::LayerVoteType voteType;
-
-    if (!mFeatures.test(Feature::kContentDetection) ||
-        layer->getWindowType() == WindowType::STATUS_BAR) {
-        voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
-    } else if (layer->getWindowType() == WindowType::WALLPAPER) {
-        // Running Wallpaper at Min is considered as part of content detection.
-        voteType = scheduler::LayerHistory::LayerVoteType::Min;
-    } else {
-        voteType = scheduler::LayerHistory::LayerVoteType::Heuristic;
-    }
-
     // If the content detection feature is off, we still keep the layer history,
     // since we use it for other features (like Frame Rate API), so layers
     // still need to be registered.
-    mLayerHistory.registerLayer(layer, voteType);
+    mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection));
 }
 
 void Scheduler::deregisterLayer(Layer* layer) {
     mLayerHistory.deregisterLayer(layer);
 }
 
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        if (!mRefreshRateConfigs->canSwitch()) return;
+    if (pacesetterSelectorPtr()->canSwitch()) {
+        mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
     }
-
-    mLayerHistory.record(layer, presentTime, systemTime(), updateType);
 }
 
 void Scheduler::setModeChangePending(bool pending) {
     mLayerHistory.setModeChangePending(pending);
 }
 
+void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) {
+    mLayerHistory.setDefaultFrameRateCompatibility(layer,
+                                                   mFeatures.test(Feature::kContentDetection));
+}
+
 void Scheduler::chooseRefreshRateForContent() {
-    const auto configs = holdRefreshRateConfigs();
-    if (!configs->canSwitch()) return;
+    const auto selectorPtr = pacesetterSelectorPtr();
+    if (!selectorPtr->canSwitch()) return;
 
     ATRACE_CALL();
 
-    LayerHistory::Summary summary = mLayerHistory.summarize(*configs, systemTime());
+    LayerHistory::Summary summary = mLayerHistory.summarize(*selectorPtr, systemTime());
     applyPolicy(&Policy::contentRequirements, std::move(summary));
 }
 
 void Scheduler::resetIdleTimer() {
-    std::scoped_lock lock(mRefreshRateConfigsLock);
-    mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
+    pacesetterSelectorPtr()->resetIdleTimer();
 }
 
 void Scheduler::onTouchHint() {
     if (mTouchTimer) {
         mTouchTimer->reset();
-
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true);
+        pacesetterSelectorPtr()->resetKernelIdleTimer();
     }
 }
 
-void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
-    {
+void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) {
+    const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) {
+        ftl::FakeGuard guard(mDisplayLock);
+        return id == mPacesetterDisplayId;
+    }();
+    if (isPacesetter) {
+        // TODO (b/255657128): This needs to be handled per display.
         std::lock_guard<std::mutex> lock(mPolicyLock);
         mPolicy.displayPowerMode = powerMode;
     }
-    mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
+    {
+        std::scoped_lock lock(mDisplayLock);
+        auto vsyncSchedule = getVsyncScheduleLocked(id);
+        LOG_ALWAYS_FATAL_IF(!vsyncSchedule);
+        vsyncSchedule->getController().setDisplayPowerMode(powerMode);
+    }
+    if (!isPacesetter) return;
 
     if (mDisplayPowerTimer) {
         mDisplayPowerTimer->reset();
@@ -575,15 +581,34 @@
     mLayerHistory.clear();
 }
 
+auto Scheduler::getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt) const
+        -> ConstVsyncSchedulePtr {
+    std::scoped_lock lock(mDisplayLock);
+    return getVsyncScheduleLocked(idOpt);
+}
+
+auto Scheduler::getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt) const
+        -> ConstVsyncSchedulePtr {
+    ftl::FakeGuard guard(kMainThreadContext);
+
+    if (!idOpt) {
+        LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!");
+        idOpt = mPacesetterDisplayId;
+    }
+
+    const auto displayOpt = mDisplays.get(*idOpt);
+    if (!displayOpt) {
+        return nullptr;
+    }
+    return displayOpt->get().schedulePtr;
+}
+
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
     ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
     // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
     // magic number
-    const Fps refreshRate = [&] {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps();
-    }();
+    const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps();
 
     constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
     using namespace fps_approx_ops;
@@ -592,12 +617,17 @@
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        resyncToHardwareVsync(true /* makeAvailable */, refreshRate);
+        resyncAllToHardwareVsync(true /* allowToEnable */);
     } else if (state == TimerState::Expired && refreshRate <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
         // need to update the VsyncController model anyway.
-        disableHardwareVsync(false /* makeUnavailable */);
+        std::scoped_lock lock(mDisplayLock);
+        ftl::FakeGuard guard(kMainThreadContext);
+        for (const auto& [_, display] : mDisplays) {
+            constexpr bool kDisallow = false;
+            display.schedulePtr->disableHardwareVsync(mSchedulerCallback, kDisallow);
+        }
     }
 
     mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
@@ -625,78 +655,186 @@
     ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
-void Scheduler::dump(std::string& result) const {
-    using base::StringAppendF;
-
-    StringAppendF(&result, "+  Touch timer: %s\n",
-                  mTouchTimer ? mTouchTimer->dump().c_str() : "off");
-    StringAppendF(&result, "+  Content detection: %s %s\n\n",
-                  mFeatures.test(Feature::kContentDetection) ? "on" : "off",
-                  mLayerHistory.dump().c_str());
-
-    mFrameRateOverrideMappings.dump(result);
+void Scheduler::dump(utils::Dumper& dumper) const {
+    using namespace std::string_view_literals;
 
     {
-        std::lock_guard lock(mHWVsyncLock);
-        StringAppendF(&result,
-                      "mScreenAcquired=%d mPrimaryHWVsyncEnabled=%d mHWVsyncAvailable=%d\n",
-                      mScreenAcquired.load(), mPrimaryHWVsyncEnabled, mHWVsyncAvailable);
+        utils::Dumper::Section section(dumper, "Features"sv);
+
+        for (Feature feature : ftl::enum_range<Feature>()) {
+            if (const auto flagOpt = ftl::flag_name(feature)) {
+                dumper.dump(flagOpt->substr(1), mFeatures.test(feature));
+            }
+        }
     }
+    {
+        utils::Dumper::Section section(dumper, "Policy"sv);
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+            dumper.dump("pacesetterDisplayId"sv, mPacesetterDisplayId);
+        }
+        dumper.dump("layerHistory"sv, mLayerHistory.dump());
+        dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval));
+        dumper.dump("displayPowerTimer"sv, mDisplayPowerTimer.transform(&OneShotTimer::interval));
+    }
+
+    mFrameRateOverrideMappings.dump(dumper);
+    dumper.eol();
 }
 
 void Scheduler::dumpVsync(std::string& out) const {
-    mVsyncSchedule->dump(out);
+    std::scoped_lock lock(mDisplayLock);
+    ftl::FakeGuard guard(kMainThreadContext);
+    if (mPacesetterDisplayId) {
+        base::StringAppendF(&out, "VsyncSchedule for pacesetter %s:\n",
+                            to_string(*mPacesetterDisplayId).c_str());
+        getVsyncScheduleLocked()->dump(out);
+    }
+    for (auto& [id, display] : mDisplays) {
+        if (id == mPacesetterDisplayId) {
+            continue;
+        }
+        base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str());
+        display.schedulePtr->dump(out);
+    }
 }
 
 bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
+    if (consideredSignals.idle) return false;
 
-    // we always update mFrameRateOverridesByContent here
-    // supportsFrameRateOverridesByContent will be checked
-    // when getting FrameRateOverrides from mFrameRateOverrideMappings
-    if (!consideredSignals.idle) {
-        const auto frameRateOverrides =
-                refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
-                                                          displayRefreshRate, consideredSignals);
-        return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+    const auto frameRateOverrides =
+            pacesetterSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements,
+                                                           displayRefreshRate, consideredSignals);
+
+    // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying
+    // the FrameRateOverrideMappings rather than here.
+    return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
+}
+
+void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
+
+    {
+        std::scoped_lock lock(mDisplayLock);
+        pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
     }
-    return false;
+
+    applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
+}
+
+std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
+        std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+    // TODO(b/241286431): Choose the pacesetter display.
+    mPacesetterDisplayId = pacesetterIdOpt.value_or(mDisplays.begin()->first);
+    ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
+
+    std::shared_ptr<VsyncSchedule> newVsyncSchedulePtr;
+    if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
+        const Display& pacesetter = *pacesetterOpt;
+
+        pacesetter.selectorPtr->setIdleTimerCallbacks(
+                {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+                              .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+                 .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+                            .onExpired =
+                                    [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+
+        pacesetter.selectorPtr->startIdleTimer();
+
+        newVsyncSchedulePtr = pacesetter.schedulePtr;
+
+        const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getFps();
+        newVsyncSchedulePtr->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(),
+                                                   true /* force */);
+    }
+    return newVsyncSchedulePtr;
+}
+
+void Scheduler::applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule> vsyncSchedule) {
+    onNewVsyncSchedule(vsyncSchedule->getDispatch());
+    std::vector<android::EventThread*> threads;
+    {
+        std::lock_guard<std::mutex> lock(mConnectionsLock);
+        threads.reserve(mConnections.size());
+        for (auto& [_, connection] : mConnections) {
+            threads.push_back(connection.thread.get());
+        }
+    }
+    for (auto* thread : threads) {
+        thread->onNewVsyncSchedule(vsyncSchedule);
+    }
+}
+
+void Scheduler::demotePacesetterDisplay() {
+    // No need to lock for reads on kMainThreadContext.
+    if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+        pacesetterPtr->stopIdleTimer();
+        pacesetterPtr->clearIdleTimerCallbacks();
+    }
+
+    // Clear state that depends on the pacesetter's RefreshRateSelector.
+    std::scoped_lock lock(mPolicyLock);
+    mPolicy = {};
 }
 
 template <typename S, typename T>
 auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals {
-    DisplayModePtr newMode;
+    ATRACE_CALL();
+    std::vector<display::DisplayModeRequest> modeRequests;
     GlobalSignals consideredSignals;
 
     bool refreshRateChanged = false;
     bool frameRateOverridesChanged;
 
-    const auto refreshRateConfigs = holdRefreshRateConfigs();
     {
-        std::lock_guard<std::mutex> lock(mPolicyLock);
+        std::scoped_lock lock(mPolicyLock);
 
         auto& currentState = mPolicy.*statePtr;
         if (currentState == newState) return {};
         currentState = std::forward<T>(newState);
 
-        std::tie(newMode, consideredSignals) = chooseDisplayMode();
-        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
+        DisplayModeChoiceMap modeChoices;
+        ftl::Optional<FrameRateMode> modeOpt;
+        {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
 
-        if (mPolicy.mode == newMode) {
+            modeChoices = chooseDisplayModes();
+
+            // TODO(b/240743786): The pacesetter display's mode must change for any
+            // DisplayModeRequest to go through. Fix this by tracking per-display Scheduler::Policy
+            // and timers.
+            std::tie(modeOpt, consideredSignals) =
+                    modeChoices.get(*mPacesetterDisplayId)
+                            .transform([](const DisplayModeChoice& choice) {
+                                return std::make_pair(choice.mode, choice.consideredSignals);
+                            })
+                            .value();
+        }
+
+        modeRequests.reserve(modeChoices.size());
+        for (auto& [id, choice] : modeChoices) {
+            modeRequests.emplace_back(
+                    display::DisplayModeRequest{.mode = std::move(choice.mode),
+                                                .emitEvent = !choice.consideredSignals.idle});
+        }
+
+        frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, modeOpt->fps);
+
+        if (mPolicy.modeOpt != modeOpt) {
+            mPolicy.modeOpt = modeOpt;
+            refreshRateChanged = true;
+        } else {
             // We don't need to change the display mode, but we might need to send an event
             // about a mode change, since it was suppressed if previously considered idle.
             if (!consideredSignals.idle) {
                 dispatchCachedReportedMode();
             }
-        } else {
-            mPolicy.mode = newMode;
-            refreshRateChanged = true;
         }
     }
     if (refreshRateChanged) {
-        mSchedulerCallback.requestDisplayMode(std::move(newMode),
-                                              consideredSignals.idle ? DisplayModeEvent::None
-                                                                     : DisplayModeEvent::Changed);
+        mSchedulerCallback.requestDisplayModes(std::move(modeRequests));
     }
     if (frameRateOverridesChanged) {
         mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -704,31 +842,66 @@
     return consideredSignals;
 }
 
-auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> {
+auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
     ATRACE_CALL();
 
-    const auto configs = holdRefreshRateConfigs();
+    using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
+    display::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
+    const auto globalSignals = makeGlobalSignals();
+    Fps pacesetterFps;
 
-    // If Display Power is not in normal operation we want to be in performance mode. When coming
-    // back to normal mode, a grace period is given with DisplayPowerTimer.
-    if (mDisplayPowerTimer &&
-        (mPolicy.displayPowerMode != hal::PowerMode::ON ||
-         mPolicy.displayPowerTimer == TimerState::Reset)) {
-        constexpr GlobalSignals kNoSignals;
-        return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
+    for (const auto& [id, display] : mDisplays) {
+        auto rankedFrameRates =
+                display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
+                                                         globalSignals);
+        if (id == *mPacesetterDisplayId) {
+            pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
+        }
+        perDisplayRanking.push_back(std::move(rankedFrameRates));
     }
 
-    const GlobalSignals signals{.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
-                                .idle = mPolicy.idleTimer == TimerState::Expired};
+    DisplayModeChoiceMap modeChoices;
+    using fps_approx_ops::operator==;
 
-    return configs->getBestRefreshRate(mPolicy.contentRequirements, signals);
+    for (auto& [rankings, signals] : perDisplayRanking) {
+        const auto chosenFrameRateMode =
+                ftl::find_if(rankings,
+                             [&](const auto& ranking) {
+                                 return ranking.frameRateMode.fps == pacesetterFps;
+                             })
+                        .transform([](const auto& scoredFrameRate) {
+                            return scoredFrameRate.get().frameRateMode;
+                        })
+                        .value_or(rankings.front().frameRateMode);
+
+        modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
+                                DisplayModeChoice{chosenFrameRateMode, signals});
+    }
+    return modeChoices;
 }
 
-DisplayModePtr Scheduler::getPreferredDisplayMode() {
+GlobalSignals Scheduler::makeGlobalSignals() const {
+    const bool powerOnImminent = mDisplayPowerTimer &&
+            (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+             mPolicy.displayPowerTimer == TimerState::Reset);
+
+    return {.touch = mTouchTimer && mPolicy.touch == TouchState::Active,
+            .idle = mPolicy.idleTimer == TimerState::Expired,
+            .powerOnImminent = powerOnImminent};
+}
+
+FrameRateMode Scheduler::getPreferredDisplayMode() {
     std::lock_guard<std::mutex> lock(mPolicyLock);
+    const auto frameRateMode =
+            pacesetterSelectorPtr()
+                    ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
+                    .ranking.front()
+                    .frameRateMode;
+
     // Make sure the stored mode is up to date.
-    mPolicy.mode = chooseDisplayMode().first;
-    return mPolicy.mode;
+    mPolicy.modeOpt = frameRateMode;
+
+    return frameRateMode;
 }
 
 void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
@@ -774,11 +947,4 @@
     mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
 }
 
-std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
-        nsecs_t expectedPresentTime) const {
-    const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
-    const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
-    return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
-}
-
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a8043bf..f13c878 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -22,8 +22,8 @@
 #include <future>
 #include <memory>
 #include <mutex>
-#include <optional>
 #include <unordered_map>
+#include <utility>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
@@ -32,15 +32,24 @@
 #include <ui/GraphicTypes.h>
 #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
 
+#include <ftl/fake_guard.h>
+#include <ftl/optional.h>
 #include <scheduler/Features.h>
+#include <scheduler/Time.h>
+#include <scheduler/VsyncConfig.h>
+#include <ui/DisplayId.h>
 
+#include "Display/DisplayMap.h"
+#include "Display/DisplayModeRequest.h"
 #include "EventThread.h"
 #include "FrameRateOverrideMappings.h"
+#include "ISchedulerCallback.h"
 #include "LayerHistory.h"
 #include "MessageQueue.h"
 #include "OneShotTimer.h"
-#include "RefreshRateConfigs.h"
-#include "VsyncSchedule.h"
+#include "RefreshRateSelector.h"
+#include "Utils/Dumper.h"
+#include "VsyncModulator.h"
 
 namespace android::scheduler {
 
@@ -74,7 +83,6 @@
 namespace android {
 
 class FenceTime;
-class InjectVSyncSource;
 
 namespace frametimeline {
 class TokenManager;
@@ -82,39 +90,40 @@
 
 namespace scheduler {
 
-struct ISchedulerCallback {
-    using DisplayModeEvent = scheduler::DisplayModeEvent;
+using GlobalSignals = RefreshRateSelector::GlobalSignals;
 
-    virtual void setVsyncEnabled(bool) = 0;
-    virtual void requestDisplayMode(DisplayModePtr, DisplayModeEvent) = 0;
-    virtual void kernelTimerChanged(bool expired) = 0;
-    virtual void triggerOnFrameRateOverridesChanged() = 0;
+class VsyncSchedule;
 
-protected:
-    ~ISchedulerCallback() = default;
-};
-
-class Scheduler : impl::MessageQueue {
-    using Impl = impl::MessageQueue;
+class Scheduler : android::impl::MessageQueue {
+    using Impl = android::impl::MessageQueue;
 
 public:
-    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
+    Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
     virtual ~Scheduler();
 
     void startTimers();
-    void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs>)
-            EXCLUDES(mRefreshRateConfigsLock);
+
+    // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+    void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+
+    using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
+
+    using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>;
+    using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>;
+
+    void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+    void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     void run();
 
-    void createVsyncSchedule(FeatureFlags);
-
     using Impl::initVsync;
-    using Impl::setInjector;
 
     using Impl::getScheduledFrameTime;
     using Impl::setDuration;
 
+    using Impl::scheduleConfigure;
     using Impl::scheduleFrame;
 
     // Schedule an asynchronous or synchronous task on the main thread.
@@ -125,21 +134,33 @@
         return std::move(future);
     }
 
-    ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
-                                      std::chrono::nanoseconds workDuration,
-                                      std::chrono::nanoseconds readyDuration,
-                                      impl::EventThread::InterceptVSyncsCallback);
+    template <typename F, typename T = std::invoke_result_t<F>>
+    [[nodiscard]] std::future<T> scheduleDelayed(F&& f, nsecs_t uptimeDelay) {
+        auto [task, future] = makeTask(std::move(f));
+        postMessageDelayed(std::move(task), uptimeDelay);
+        return std::move(future);
+    }
+
+    enum class Cycle {
+        Render,       // Surface rendering.
+        LastComposite // Ahead of display compositing by one refresh period.
+    };
+
+    ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*,
+                                       std::chrono::nanoseconds workDuration,
+                                       std::chrono::nanoseconds readyDuration);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(
-            ConnectionHandle, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
+            ConnectionHandle, EventRegistrationFlags eventRegistration = {},
+            const sp<IBinder>& layerHandle = nullptr);
 
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
-    void onScreenAcquired(ConnectionHandle);
-    void onScreenReleased(ConnectionHandle);
+    void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&);
+
+    void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
 
     void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId)
             EXCLUDES(mConnectionsLock);
@@ -148,60 +169,98 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    DisplayStatInfo getDisplayStatInfo(nsecs_t now);
+    VsyncModulator& vsyncModulator() { return *mVsyncModulator; }
 
-    // Returns injector handle if injection has toggled, or an invalid handle otherwise.
-    ConnectionHandle enableVSyncInjection(bool enable);
-    // Returns false if injection is disabled.
-    bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp);
-    void enableHardwareVsync();
-    void disableHardwareVsync(bool makeUnavailable);
+    // In some cases, we should only modulate for the pacesetter display. In those
+    // cases, the caller should pass in the relevant display, and the method
+    // will no-op if it's not the pacesetter. Other cases are not specific to a
+    // display.
+    template <typename... Args,
+              typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
+    void modulateVsync(std::optional<PhysicalDisplayId> id, Handler handler, Args... args) {
+        if (id) {
+            std::scoped_lock lock(mDisplayLock);
+            ftl::FakeGuard guard(kMainThreadContext);
+            if (id != mPacesetterDisplayId) {
+                return;
+            }
+        }
+
+        if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+            setVsyncConfig(*config, getPacesetterVsyncPeriod());
+        }
+    }
+
+    void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
+
+    // Sets the render rate for the scheduler to run at.
+    void setRenderRate(PhysicalDisplayId, Fps);
+
+    void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+    void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext);
 
     // Resyncs the scheduler to hardware vsync.
-    // If makeAvailable is true, then hardware vsync will be turned on.
+    // If allowToEnable is true, then hardware vsync will be turned on.
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
-    void resyncToHardwareVsync(bool makeAvailable, Fps refreshRate);
-    void resync() EXCLUDES(mRefreshRateConfigsLock);
+    // If refreshRate is nullopt, use the existing refresh rate of the display.
+    void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable,
+                               std::optional<Fps> refreshRate = std::nullopt)
+            EXCLUDES(mDisplayLock) {
+        std::scoped_lock lock(mDisplayLock);
+        ftl::FakeGuard guard(kMainThreadContext);
+        resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
+    }
+    void resync() EXCLUDES(mDisplayLock);
     void forceNextResync() { mLastResyncTime = 0; }
 
-    // Passes a vsync sample to VsyncController. periodFlushed will be true if
-    // VsyncController detected that the vsync period changed, and false otherwise.
-    void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                         bool* periodFlushed);
-    void addPresentFence(std::shared_ptr<FenceTime>);
+    // Passes a vsync sample to VsyncController. Returns true if
+    // VsyncController detected that the vsync period changed and false
+    // otherwise.
+    bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp,
+                         std::optional<nsecs_t> hwcVsyncPeriod);
+    void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock)
+            REQUIRES(kMainThreadContext);
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
-    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType)
-            EXCLUDES(mRefreshRateConfigsLock);
+    void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
+                            LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
+    void setDefaultFrameRateCompatibility(Layer*);
     void deregisterLayer(Layer*);
 
     // Detects content using layer history, and selects a matching refresh rate.
-    void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock);
+    void chooseRefreshRateForContent() EXCLUDES(mDisplayLock);
 
     void resetIdleTimer();
 
     // Indicates that touch interaction is taking place.
     void onTouchHint();
 
-    void setDisplayPowerMode(hal::PowerMode powerMode);
+    void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode)
+            REQUIRES(kMainThreadContext);
 
-    VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
+    ConstVsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> = std::nullopt) const
+            EXCLUDES(mDisplayLock);
+
+    VsyncSchedulePtr getVsyncSchedule(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+            EXCLUDES(mDisplayLock) {
+        return std::const_pointer_cast<VsyncSchedule>(std::as_const(*this).getVsyncSchedule(idOpt));
+    }
 
     // Returns true if a given vsync timestamp is considered valid vsync
     // for a given uid
-    bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+    bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const;
 
-    std::chrono::steady_clock::time_point getPreviousVsyncFrom(nsecs_t expectedPresentTime) const;
+    bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const;
 
-    void dump(std::string&) const;
+    void dump(utils::Dumper&) const;
     void dump(ConnectionHandle, std::string&) const;
-    void dumpVsync(std::string&) const;
+    void dumpVsync(std::string&) const EXCLUDES(mDisplayLock);
 
-    // Get the appropriate refresh for current conditions.
-    DisplayModePtr getPreferredDisplayMode();
+    // Returns the preferred refresh rate and frame rate for the pacesetter display.
+    FrameRateMode getPreferredDisplayMode();
 
     // Notifies the scheduler about a refresh rate timeline change.
     void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
@@ -214,11 +273,6 @@
 
     size_t getEventThreadConnectionCount(ConnectionHandle handle);
 
-    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name,
-                                                           std::chrono::nanoseconds workDuration,
-                                                           std::chrono::nanoseconds readyDuration,
-                                                           bool traceVsync = true);
-
     // Stores the preferred refresh rate that an app should run at.
     // FrameRateOverride.refreshRateHz == 0 means no preference.
     void setPreferredRefreshRateForUid(FrameRateOverride);
@@ -226,11 +280,14 @@
     void setGameModeRefreshRateForUid(FrameRateOverride);
 
     // Retrieves the overridden refresh rate for a given uid.
-    std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mRefreshRateConfigsLock);
+    std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
 
-    nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs->getActiveMode()->getFps().getPeriodNsecs();
+    Period getPacesetterVsyncPeriod() const EXCLUDES(mDisplayLock) {
+        return pacesetterSelectorPtr()->getActiveMode().fps.getPeriod();
+    }
+
+    Fps getPacesetterRefreshRate() const EXCLUDES(mDisplayLock) {
+        return pacesetterSelectorPtr()->getActiveMode().fps;
     }
 
     // Returns the framerate of the layer with the given sequence ID
@@ -245,20 +302,49 @@
     enum class TimerState { Reset, Expired };
     enum class TouchState { Inactive, Active };
 
+    // impl::MessageQueue overrides:
+    void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override;
+
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
     sp<EventThreadConnection> createConnectionInternal(
-            EventThread*, ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
+            EventThread*, EventRegistrationFlags eventRegistration = {},
+            const sp<IBinder>& layerHandle = nullptr);
 
     // Update feature state machine to given state when corresponding timer resets or expires.
-    void kernelIdleTimerCallback(TimerState) EXCLUDES(mRefreshRateConfigsLock);
+    void kernelIdleTimerCallback(TimerState) EXCLUDES(mDisplayLock);
     void idleTimerCallback(TimerState);
     void touchTimerCallback(TimerState);
     void displayPowerTimerCallback(TimerState);
 
-    void setVsyncPeriod(nsecs_t period);
+    void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable,
+                                     std::optional<Fps> refreshRate = std::nullopt)
+            REQUIRES(kMainThreadContext, mDisplayLock);
+    void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
+    void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
 
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+    // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
+    // The new `mPacesetterDisplayId` is never `std::nullopt`.
+    void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+    // Changes to the displays (e.g. registering and unregistering) must be made
+    // while mDisplayLock is locked, and the new pacesetter then must be promoted while
+    // mDisplayLock is still locked. However, a new pacesetter means that
+    // MessageQueue and EventThread need to use the new pacesetter's
+    // VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
+    // or else we may deadlock with EventThread.
+    std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
+            std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+            REQUIRES(kMainThreadContext, mDisplayLock);
+    void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
+
+    // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
+    // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
+    void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+
+    void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr)
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
 
     struct Policy;
 
@@ -267,22 +353,38 @@
     template <typename S, typename T>
     GlobalSignals applyPolicy(S Policy::*, T&&) EXCLUDES(mPolicyLock);
 
-    // Returns the display mode that fulfills the policy, and the signals that were considered.
-    std::pair<DisplayModePtr, GlobalSignals> chooseDisplayMode() REQUIRES(mPolicyLock);
+    struct DisplayModeChoice {
+        DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
+              : mode(std::move(mode)), consideredSignals(consideredSignals) {}
+
+        FrameRateMode mode;
+        GlobalSignals consideredSignals;
+
+        bool operator==(const DisplayModeChoice& other) const {
+            return mode == other.mode && consideredSignals == other.consideredSignals;
+        }
+
+        // For tests.
+        friend std::ostream& operator<<(std::ostream& stream, const DisplayModeChoice& choice) {
+            return stream << '{' << to_string(*choice.mode.modePtr) << " considering "
+                          << choice.consideredSignals.toString().c_str() << '}';
+        }
+    };
+
+    using DisplayModeChoiceMap = display::PhysicalDisplayMap<PhysicalDisplayId, DisplayModeChoice>;
+
+    // See mDisplayLock for thread safety.
+    DisplayModeChoiceMap chooseDisplayModes() const
+            REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext);
+
+    GlobalSignals makeGlobalSignals() const REQUIRES(mPolicyLock);
 
     bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) REQUIRES(mPolicyLock);
 
-    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+    void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
 
-    impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
-            EXCLUDES(mRefreshRateConfigsLock);
-    impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
-
-    std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
-            EXCLUDES(mRefreshRateConfigsLock) {
-        std::scoped_lock lock(mRefreshRateConfigsLock);
-        return mRefreshRateConfigs;
-    }
+    android::impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const;
+    android::impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
@@ -294,31 +396,86 @@
     mutable std::mutex mConnectionsLock;
     std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
 
-    bool mInjectVSyncs = false;
-    InjectVSyncSource* mVSyncInjector = nullptr;
-    ConnectionHandle mInjectorConnectionHandle;
-
-    mutable std::mutex mHWVsyncLock;
-    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
-    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
+    ConnectionHandle mAppConnectionHandle;
+    ConnectionHandle mSfConnectionHandle;
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
     const FeatureFlags mFeatures;
-    std::optional<VsyncSchedule> mVsyncSchedule;
+
+    // Shifts the VSYNC phase during certain transactions and refresh rate changes.
+    const sp<VsyncModulator> mVsyncModulator;
 
     // Used to choose refresh rate if content detection is enabled.
     LayerHistory mLayerHistory;
 
     // Timer used to monitor touch events.
-    std::optional<OneShotTimer> mTouchTimer;
+    ftl::Optional<OneShotTimer> mTouchTimer;
     // Timer used to monitor display power mode.
-    std::optional<OneShotTimer> mDisplayPowerTimer;
+    ftl::Optional<OneShotTimer> mDisplayPowerTimer;
 
     ISchedulerCallback& mSchedulerCallback;
 
+    // mDisplayLock may be locked while under mPolicyLock.
     mutable std::mutex mPolicyLock;
 
+    // Only required for reads outside kMainThreadContext. kMainThreadContext is the only writer, so
+    // must lock for writes but not reads. See also mPolicyLock for locking order.
+    mutable std::mutex mDisplayLock;
+
+    struct Display {
+        Display(RefreshRateSelectorPtr selectorPtr, VsyncSchedulePtr schedulePtr)
+              : selectorPtr(std::move(selectorPtr)), schedulePtr(std::move(schedulePtr)) {}
+
+        // Effectively const except in move constructor.
+        RefreshRateSelectorPtr selectorPtr;
+        VsyncSchedulePtr schedulePtr;
+    };
+
+    using DisplayRef = std::reference_wrapper<Display>;
+    using ConstDisplayRef = std::reference_wrapper<const Display>;
+
+    display::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays GUARDED_BY(mDisplayLock)
+            GUARDED_BY(kMainThreadContext);
+
+    ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock)
+            GUARDED_BY(kMainThreadContext);
+
+    ftl::Optional<DisplayRef> pacesetterDisplayLocked() REQUIRES(mDisplayLock) {
+        return static_cast<const Scheduler*>(this)->pacesetterDisplayLocked().transform(
+                [](const Display& display) { return std::ref(const_cast<Display&>(display)); });
+    }
+
+    ftl::Optional<ConstDisplayRef> pacesetterDisplayLocked() const REQUIRES(mDisplayLock) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return mPacesetterDisplayId.and_then([this](PhysicalDisplayId pacesetterId)
+                                                     REQUIRES(mDisplayLock, kMainThreadContext) {
+                                                         return mDisplays.get(pacesetterId);
+                                                     });
+    }
+
+    RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) {
+        std::scoped_lock lock(mDisplayLock);
+        return pacesetterSelectorPtrLocked();
+    }
+
+    RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return pacesetterDisplayLocked()
+                .transform([](const Display& display) { return display.selectorPtr; })
+                .or_else([] { return std::optional<RefreshRateSelectorPtr>(nullptr); })
+                .value();
+    }
+
+    ConstVsyncSchedulePtr getVsyncScheduleLocked(
+            std::optional<PhysicalDisplayId> = std::nullopt) const REQUIRES(mDisplayLock);
+
+    VsyncSchedulePtr getVsyncScheduleLocked(std::optional<PhysicalDisplayId> idOpt = std::nullopt)
+            REQUIRES(mDisplayLock) {
+        return std::const_pointer_cast<VsyncSchedule>(
+                static_cast<const Scheduler*>(this)->getVsyncScheduleLocked(idOpt));
+    }
+
     struct Policy {
         // Policy for choosing the display mode.
         LayerHistory::Summary contentRequirements;
@@ -328,29 +485,23 @@
         hal::PowerMode displayPowerMode = hal::PowerMode::ON;
 
         // Chosen display mode.
-        DisplayModePtr mode;
+        ftl::Optional<FrameRateMode> modeOpt;
 
         struct ModeChangedParams {
             ConnectionHandle handle;
-            DisplayModePtr mode;
+            FrameRateMode mode;
         };
 
         // Parameters for latest dispatch of mode change event.
         std::optional<ModeChangedParams> cachedModeChangedParams;
     } mPolicy GUARDED_BY(mPolicyLock);
 
-    mutable std::mutex mRefreshRateConfigsLock;
-    std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
-
     std::mutex mVsyncTimelineLock;
     std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
             GUARDED_BY(mVsyncTimelineLock);
     static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 
     FrameRateOverrideMappings mFrameRateOverrideMappings;
-
-    // Keeps track of whether the screen is acquired for debug
-    std::atomic<bool> mScreenAcquired = false;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 2bfe204..c3a952f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -126,6 +126,17 @@
      */
     virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
 
+    /*
+     * Update the timing information for a scheduled callback.
+     * If the callback is not scheduled, then this function does nothing.
+     *
+     * \param [in] token           The callback to schedule.
+     * \param [in] scheduleTiming  The timing information for this schedule call
+     * \return                     The expected callback time if a callback was scheduled.
+     *                             std::nullopt if the callback is not registered.
+     */
+    virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+
     /* Cancels a scheduled callback, if possible.
      *
      * \param [in] token    The callback to cancel.
@@ -144,13 +155,10 @@
     VSyncDispatch& operator=(const VSyncDispatch&) = delete;
 };
 
-/*
- * Helper class to operate on registered callbacks. It is up to user of the class to ensure
- * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
- */
 class VSyncCallbackRegistration {
 public:
-    VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback, std::string callbackName);
+    VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch>, VSyncDispatch::Callback,
+                              std::string callbackName);
     ~VSyncCallbackRegistration();
 
     VSyncCallbackRegistration(VSyncCallbackRegistration&&);
@@ -159,13 +167,17 @@
     // See documentation for VSyncDispatch::schedule.
     ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
 
+    // See documentation for VSyncDispatch::update.
+    ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming);
+
     // See documentation for VSyncDispatch::cancel.
     CancelResult cancel();
 
 private:
-    std::reference_wrapper<VSyncDispatch> mDispatch;
-    VSyncDispatch::CallbackToken mToken;
-    bool mValidToken;
+    friend class VSyncCallbackRegistrationTest;
+
+    std::shared_ptr<VSyncDispatch> mDispatch;
+    std::optional<VSyncDispatch::CallbackToken> mToken;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 27f4311..1f922f1 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -21,12 +21,16 @@
 #include <android-base/stringprintf.h>
 #include <ftl/concat.h>
 #include <utils/Trace.h>
+#include <log/log_main.h>
 
 #include <scheduler/TimeKeeper.h>
 
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncTracker.h"
 
+#undef LOG_TAG
+#define LOG_TAG "VSyncDispatch"
+
 namespace android::scheduler {
 
 using base::StringAppendF;
@@ -100,14 +104,8 @@
         return getExpectedCallbackTime(nextVsyncTime, timing);
     }
 
-    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
-            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
-             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
-    if (alreadyDispatchedForVsync) {
-        nextVsyncTime =
-                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
-        nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
-    }
+    nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime);
+    nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
 
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
@@ -123,6 +121,25 @@
     return mWorkloadUpdateInfo.has_value();
 }
 
+nsecs_t VSyncDispatchTimerQueueEntry::adjustVsyncIfNeeded(VSyncTracker& tracker,
+                                                          nsecs_t nextVsyncTime) const {
+    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+    const nsecs_t currentPeriod = tracker.currentPeriod();
+    bool const nextVsyncTooClose = mLastDispatchTime &&
+            (nextVsyncTime - *mLastDispatchTime + mMinVsyncDistance) <= currentPeriod;
+    if (alreadyDispatchedForVsync) {
+        return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+    }
+
+    if (nextVsyncTooClose) {
+        return tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + currentPeriod);
+    }
+
+    return nextVsyncTime;
+}
+
 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
     if (!mArmedInfo && !mWorkloadUpdateInfo) {
         return;
@@ -136,7 +153,9 @@
     const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
     const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);
 
-    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
+    const auto nextVsyncTime =
+            adjustVsyncIfNeeded(tracker, /*nextVsyncTime*/
+                                tracker.nextAnticipatedVSyncTimeFrom(earliestVsync));
     const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
     const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
 
@@ -200,16 +219,20 @@
 }
 
 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
-                                                 VSyncTracker& tracker, nsecs_t timerSlack,
-                                                 nsecs_t minVsyncDistance)
+                                                 VsyncSchedule::TrackerPtr tracker,
+                                                 nsecs_t timerSlack, nsecs_t minVsyncDistance)
       : mTimeKeeper(std::move(tk)),
-        mTracker(tracker),
+        mTracker(std::move(tracker)),
         mTimerSlack(timerSlack),
         mMinVsyncDistance(minVsyncDistance) {}
 
 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
     std::lock_guard lock(mMutex);
     cancelTimer();
+    for (auto& [_, entry] : mCallbacks) {
+        ALOGE("Forgot to unregister a callback on VSyncDispatch!");
+        entry->ensureNotRunning();
+    }
 }
 
 void VSyncDispatchTimerQueue::cancelTimer() {
@@ -240,7 +263,7 @@
         }
 
         if (it != skipUpdateIt) {
-            callback->update(mTracker, now);
+            callback->update(*mTracker, now);
         }
         auto const wakeupTime = *callback->wakeupTime();
         if (!min || *min > wakeupTime) {
@@ -332,38 +355,54 @@
 
 ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                  ScheduleTiming scheduleTiming) {
-    ScheduleResult result;
-    {
-        std::lock_guard lock(mMutex);
+    std::lock_guard lock(mMutex);
+    return scheduleLocked(token, scheduleTiming);
+}
 
-        auto it = mCallbacks.find(token);
-        if (it == mCallbacks.end()) {
-            return result;
-        }
-        auto& callback = it->second;
-        auto const now = mTimeKeeper->now();
+ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token,
+                                                       ScheduleTiming scheduleTiming) {
+    auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return {};
+    }
+    auto& callback = it->second;
+    auto const now = mTimeKeeper->now();
 
-        /* If the timer thread will run soon, we'll apply this work update via the callback
-         * timer recalculation to avoid cancelling a callback that is about to fire. */
-        auto const rearmImminent = now > mIntendedWakeupTime;
-        if (CC_UNLIKELY(rearmImminent)) {
-            callback->addPendingWorkloadUpdate(scheduleTiming);
-            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
-        }
+    /* If the timer thread will run soon, we'll apply this work update via the callback
+     * timer recalculation to avoid cancelling a callback that is about to fire. */
+    auto const rearmImminent = now > mIntendedWakeupTime;
+    if (CC_UNLIKELY(rearmImminent)) {
+        callback->addPendingWorkloadUpdate(scheduleTiming);
+        return getExpectedCallbackTime(*mTracker, now, scheduleTiming);
+    }
 
-        result = callback->schedule(scheduleTiming, mTracker, now);
-        if (!result.has_value()) {
-            return result;
-        }
+    const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
+    if (!result.has_value()) {
+        return {};
+    }
 
-        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
-            rearmTimerSkippingUpdateFor(now, it);
-        }
+    if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
+        rearmTimerSkippingUpdateFor(now, it);
     }
 
     return result;
 }
 
+ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) {
+    std::lock_guard lock(mMutex);
+    const auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return {};
+    }
+
+    auto& callback = it->second;
+    if (!callback->targetVsync().has_value()) {
+        return {};
+    }
+
+    return scheduleLocked(token, scheduleTiming);
+}
+
 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
     std::lock_guard lock(mMutex);
 
@@ -403,44 +442,48 @@
     }
 }
 
-VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch,
                                                      VSyncDispatch::Callback callback,
                                                      std::string callbackName)
-      : mDispatch(dispatch),
-        mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
-        mValidToken(true) {}
+      : mDispatch(std::move(dispatch)),
+        mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))) {}
 
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
-      : mDispatch(other.mDispatch),
-        mToken(std::move(other.mToken)),
-        mValidToken(std::move(other.mValidToken)) {
-    other.mValidToken = false;
-}
+      : mDispatch(std::move(other.mDispatch)), mToken(std::exchange(other.mToken, std::nullopt)) {}
 
 VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+    if (this == &other) return *this;
+    if (mToken) {
+        mDispatch->unregisterCallback(*mToken);
+    }
     mDispatch = std::move(other.mDispatch);
-    mToken = std::move(other.mToken);
-    mValidToken = std::move(other.mValidToken);
-    other.mValidToken = false;
+    mToken = std::exchange(other.mToken, std::nullopt);
     return *this;
 }
 
 VSyncCallbackRegistration::~VSyncCallbackRegistration() {
-    if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+    if (mToken) mDispatch->unregisterCallback(*mToken);
 }
 
 ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
-    if (!mValidToken) {
+    if (!mToken) {
         return std::nullopt;
     }
-    return mDispatch.get().schedule(mToken, scheduleTiming);
+    return mDispatch->schedule(*mToken, scheduleTiming);
+}
+
+ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
+    if (!mToken) {
+        return std::nullopt;
+    }
+    return mDispatch->update(*mToken, scheduleTiming);
 }
 
 CancelResult VSyncCallbackRegistration::cancel() {
-    if (!mValidToken) {
+    if (!mToken) {
         return CancelResult::Error;
     }
-    return mDispatch.get().cancel(mToken);
+    return mDispatch->cancel(*mToken);
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 4923031..6499d69 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -26,11 +26,11 @@
 #include <android-base/thread_annotations.h>
 
 #include "VSyncDispatch.h"
+#include "VsyncSchedule.h"
 
 namespace android::scheduler {
 
 class TimeKeeper;
-class VSyncTracker;
 
 // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
 // VSyncDispatchTimerQueue hoisted to public for unit testing.
@@ -84,6 +84,8 @@
     void dump(std::string& result) const;
 
 private:
+    nsecs_t adjustVsyncIfNeeded(VSyncTracker& tracker, nsecs_t nextVsyncTime) const;
+
     const std::string mName;
     const VSyncDispatch::Callback mCallback;
 
@@ -118,13 +120,14 @@
     //                                  should be grouped into one wakeup.
     // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
     //                                  vsyncs are considered the same vsync event.
-    VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VSyncTracker&, nsecs_t timerSlack,
-                            nsecs_t minVsyncDistance);
+    VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VsyncSchedule::TrackerPtr,
+                            nsecs_t timerSlack, nsecs_t minVsyncDistance);
     ~VSyncDispatchTimerQueue();
 
     CallbackToken registerCallback(Callback, std::string callbackName) final;
     void unregisterCallback(CallbackToken) final;
     ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
+    ScheduleResult update(CallbackToken, ScheduleTiming) final;
     CancelResult cancel(CallbackToken) final;
     void dump(std::string&) const final;
 
@@ -141,10 +144,11 @@
     void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
             REQUIRES(mMutex);
     void cancelTimer() REQUIRES(mMutex);
+    ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
 
     static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
     std::unique_ptr<TimeKeeper> const mTimeKeeper;
-    VSyncTracker& mTracker;
+    VsyncSchedule::TrackerPtr mTracker;
     nsecs_t const mTimerSlack;
     nsecs_t const mMinVsyncDistance;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 77782e9..e969fdc 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -31,10 +31,11 @@
 #include <android-base/stringprintf.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
+#include <ftl/concat.h>
+#include <gui/TraceUtils.h>
 #include <utils/Log.h>
-#include <utils/Trace.h>
 
-#include "RefreshRateConfigs.h"
+#include "RefreshRateSelector.h"
 #include "VSyncPredictor.h"
 
 namespace android::scheduler {
@@ -45,9 +46,10 @@
 
 VSyncPredictor::~VSyncPredictor() = default;
 
-VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
+VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
                                size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
-      : mTraceOn(property_get_bool("debug.sf.vsp_trace", true)),
+      : mId(id),
+        mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
         kHistorySize(historySize),
         kMinimumSamplesForPrediction(minimumSamplesForPrediction),
         kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
@@ -57,10 +59,14 @@
 
 inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
     if (CC_UNLIKELY(mTraceOn)) {
-        ATRACE_INT64(name, value);
+        traceInt64(name, value);
     }
 }
 
+inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const {
+    ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value);
+}
+
 inline size_t VSyncPredictor::next(size_t i) const {
     return (i + 1) % mTimestamps.size();
 }
@@ -124,6 +130,8 @@
         mTimestamps[mLastTimestampIndex] = timestamp;
     }
 
+    traceInt64If("VSP-ts", timestamp);
+
     const size_t numSamples = mTimestamps.size();
     if (numSamples < kMinimumSamplesForPrediction) {
         mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
@@ -161,13 +169,13 @@
     nsecs_t meanOrdinal = 0;
 
     for (size_t i = 0; i < numSamples; i++) {
-        traceInt64If("VSP-ts", mTimestamps[i]);
-
         const auto timestamp = mTimestamps[i] - oldestTS;
         vsyncTS[i] = timestamp;
         meanTS += timestamp;
 
-        const auto ordinal = (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
+        const auto ordinal = currentPeriod == 0
+                ? 0
+                : (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
         ordinals[i] = ordinal;
         meanOrdinal += ordinal;
     }
@@ -208,16 +216,27 @@
 
     it->second = {anticipatedPeriod, intercept};
 
-    ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
-          anticipatedPeriod, intercept);
+    ALOGV("model update ts %" PRIu64 ": %" PRId64 " slope: %" PRId64 " intercept: %" PRId64,
+          mId.value, timestamp, anticipatedPeriod, intercept);
     return true;
 }
 
+auto VSyncPredictor::getVsyncSequenceLocked(nsecs_t timestamp) const -> VsyncSequence {
+    const auto vsync = nextAnticipatedVSyncTimeFromLocked(timestamp);
+    if (!mLastVsyncSequence) return {vsync, 0};
+
+    const auto [slope, _] = getVSyncPredictionModelLocked();
+    const auto [lastVsyncTime, lastVsyncSequence] = *mLastVsyncSequence;
+    const auto vsyncSequence = lastVsyncSequence +
+            static_cast<int64_t>(std::round((vsync - lastVsyncTime) / static_cast<float>(slope)));
+    return {vsync, vsyncSequence};
+}
+
 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
     auto const [slope, intercept] = getVSyncPredictionModelLocked();
 
     if (mTimestamps.empty()) {
-        traceInt64If("VSP-mode", 1);
+        traceInt64("VSP-mode", 1);
         auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
         auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
         return knownTimestamp + numPeriodsOut * mIdealPeriod;
@@ -230,7 +249,7 @@
     auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
     auto const prediction = (ordinalRequest * slope) + intercept + oldest;
 
-    traceInt64If("VSP-mode", 0);
+    traceInt64("VSP-mode", 0);
     traceInt64If("VSP-timePoint", timePoint);
     traceInt64If("VSP-prediction", prediction);
 
@@ -251,7 +270,31 @@
 
 nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
     std::lock_guard lock(mMutex);
-    return nextAnticipatedVSyncTimeFromLocked(timePoint);
+
+    // update the mLastVsyncSequence for reference point
+    mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
+
+    const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
+        if (!mRenderRate) return 0;
+
+        const auto divisor =
+                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod),
+                                                         *mRenderRate);
+        if (divisor <= 1) return 0;
+
+        const int mod = mLastVsyncSequence->seq % divisor;
+        if (mod == 0) return 0;
+
+        return divisor - mod;
+    }();
+
+    if (renderRatePhase == 0) {
+        return mLastVsyncSequence->vsyncTime;
+    }
+
+    auto const [slope, intercept] = getVSyncPredictionModelLocked();
+    const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
+    return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
 }
 
 /*
@@ -263,51 +306,36 @@
  * isVSyncInPhase(50.0, 30) = true
  */
 bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
-    struct VsyncError {
-        nsecs_t vsyncTimestamp;
-        float error;
-
-        bool operator<(const VsyncError& other) const { return error < other.error; }
-    };
-
     std::lock_guard lock(mMutex);
     const auto divisor =
-            RefreshRateConfigs::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+    return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
+}
+
+bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const {
+    const TimePoint now = TimePoint::now();
+    const auto getTimePointIn = [](TimePoint now, nsecs_t timePoint) -> float {
+        return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
+    };
+    ATRACE_FORMAT("%s timePoint in: %.2f divisor: %zu", __func__, getTimePointIn(now, timePoint),
+                  divisor);
+
     if (divisor <= 1 || timePoint == 0) {
         return true;
     }
 
     const nsecs_t period = mRateMap[mIdealPeriod].slope;
     const nsecs_t justBeforeTimePoint = timePoint - period / 2;
-    const nsecs_t dividedPeriod = mIdealPeriod / divisor;
+    const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint);
+    ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64,
+                          getTimePointIn(now, vsyncSequence.vsyncTime), vsyncSequence.seq);
+    return vsyncSequence.seq % divisor == 0;
+}
 
-    // If this is the first time we have asked about this divisor with the
-    // current vsync period, it is considered in phase and we store the closest
-    // vsync timestamp
-    const auto knownTimestampIter = mRateDivisorKnownTimestampMap.find(dividedPeriod);
-    if (knownTimestampIter == mRateDivisorKnownTimestampMap.end()) {
-        const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
-        mRateDivisorKnownTimestampMap[dividedPeriod] = vsync;
-        return true;
-    }
-
-    // Find the next N vsync timestamp where N is the divisor.
-    // One of these vsyncs will be in phase. We return the one which is
-    // the most aligned with the last known in phase vsync
-    std::vector<VsyncError> vsyncs(static_cast<size_t>(divisor));
-    const nsecs_t knownVsync = knownTimestampIter->second;
-    nsecs_t point = justBeforeTimePoint;
-    for (size_t i = 0; i < divisor; i++) {
-        const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
-        const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divisor);
-        const auto error = std::abs(std::round(numPeriods) - numPeriods);
-        vsyncs[i] = {vsync, error};
-        point = vsync + 1;
-    }
-
-    const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
-    mRateDivisorKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
-    return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+void VSyncPredictor::setRenderRate(Fps fps) {
+    ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str());
+    std::lock_guard lock(mMutex);
+    mRenderRate = fps;
 }
 
 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -321,7 +349,8 @@
 }
 
 void VSyncPredictor::setPeriod(nsecs_t period) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str());
+    traceInt64("VSP-setPeriod", period);
 
     std::lock_guard lock(mMutex);
     static constexpr size_t kSizeLimit = 30;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3181102..c01c44d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -21,6 +21,7 @@
 #include <vector>
 
 #include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
 
 #include "VSyncTracker.h"
 
@@ -29,14 +30,15 @@
 class VSyncPredictor : public VSyncTracker {
 public:
     /*
+     * \param [in] PhysicalDisplayid The display this corresponds to.
      * \param [in] idealPeriod  The initial ideal period to use.
      * \param [in] historySize  The internal amount of entries to store in the model.
      * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
      * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
      * samples that fall outlierTolerancePercent from an anticipated vsync event.
      */
-    VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction,
-                   uint32_t outlierTolerancePercent);
+    VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize,
+                   size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent);
     ~VSyncPredictor();
 
     bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
@@ -67,6 +69,8 @@
 
     bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
 
+    void setRenderRate(Fps) final EXCLUDES(mMutex);
+
     void dump(std::string& result) const final EXCLUDES(mMutex);
 
 private:
@@ -74,20 +78,28 @@
     VSyncPredictor& operator=(VSyncPredictor const&) = delete;
     void clearTimestamps() REQUIRES(mMutex);
 
-    inline void traceInt64If(const char* name, int64_t value) const;
-    bool const mTraceOn;
+    const PhysicalDisplayId mId;
 
+    inline void traceInt64If(const char* name, int64_t value) const;
+    inline void traceInt64(const char* name, int64_t value) const;
+
+    size_t next(size_t i) const REQUIRES(mMutex);
+    bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
+    Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+    nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
+    bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex);
+
+    struct VsyncSequence {
+        nsecs_t vsyncTime;
+        int64_t seq;
+    };
+    VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);
+
+    bool const mTraceOn;
     size_t const kHistorySize;
     size_t const kMinimumSamplesForPrediction;
     size_t const kOutlierTolerancePercent;
-
     std::mutex mutable mMutex;
-    size_t next(size_t i) const REQUIRES(mMutex);
-    bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
-
-    Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
-
-    nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
 
     nsecs_t mIdealPeriod GUARDED_BY(mMutex);
     std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
@@ -95,11 +107,12 @@
     // Map between ideal vsync period and the calculated model
     std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
 
-    // Map between the divided vsync period and the last known vsync timestamp
-    std::unordered_map<nsecs_t, nsecs_t> mutable mRateDivisorKnownTimestampMap GUARDED_BY(mMutex);
-
     size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+
+    std::optional<Fps> mRenderRate GUARDED_BY(mMutex);
+
+    mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index e23945d..2938aa3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -21,6 +21,8 @@
 
 #include <assert.h>
 #include <cutils/properties.h>
+#include <ftl/concat.h>
+#include <gui/TraceUtils.h>
 #include <log/log.h>
 #include <utils/Trace.h>
 
@@ -39,12 +41,13 @@
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
-VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker,
-                           size_t pendingFenceLimit, bool supportKernelIdleTimer)
-      : mClock(std::move(clock)),
+VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock,
+                           VSyncTracker& tracker, size_t pendingFenceLimit,
+                           bool supportKernelIdleTimer)
+      : mId(id),
+        mClock(std::move(clock)),
         mTracker(tracker),
         mPendingLimit(pendingFenceLimit),
-        // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes
         mSupportKernelIdleTimer(supportKernelIdleTimer) {}
 
 VSyncReactor::~VSyncReactor() = default;
@@ -114,7 +117,7 @@
 }
 
 void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
     mPeriodConfirmationInProgress = true;
     mPeriodTransitioningTo = newPeriod;
     mMoreSamplesNeeded = true;
@@ -122,18 +125,18 @@
 }
 
 void VSyncReactor::endPeriodTransition() {
-    ATRACE_CALL();
+    ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
     mPeriodTransitioningTo.reset();
     mPeriodConfirmationInProgress = false;
     mLastHwVsync.reset();
 }
 
-void VSyncReactor::startPeriodTransition(nsecs_t period) {
-    ATRACE_INT64("VSR-setPeriod", period);
+void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) {
+    ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period);
     std::lock_guard lock(mMutex);
     mLastHwVsync.reset();
 
-    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
+    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) {
         endPeriodTransition();
         setIgnorePresentFencesInternal(false);
         mMoreSamplesNeeded = false;
@@ -181,7 +184,7 @@
 
     std::lock_guard lock(mMutex);
     if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
-        ATRACE_NAME("VSR: period confirmed");
+        ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
         if (mPeriodTransitioningTo) {
             mTracker.setPeriod(*mPeriodTransitioningTo);
             *periodFlushed = true;
@@ -195,12 +198,12 @@
         endPeriodTransition();
         mMoreSamplesNeeded = mTracker.needsMoreSamples();
     } else if (mPeriodConfirmationInProgress) {
-        ATRACE_NAME("VSR: still confirming period");
+        ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value);
         mLastHwVsync = timestamp;
         mMoreSamplesNeeded = true;
         *periodFlushed = false;
     } else {
-        ATRACE_NAME("VSR: adding sample");
+        ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value);
         *periodFlushed = false;
         mTracker.addVsyncTimestamp(timestamp);
         mMoreSamplesNeeded = mTracker.needsMoreSamples();
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 4501487..f230242 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
 #include <ui/FenceTime.h>
 
 #include <scheduler/TimeKeeper.h>
@@ -37,14 +38,14 @@
 // TODO (b/145217110): consider renaming.
 class VSyncReactor : public VsyncController {
 public:
-    VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
-                 bool supportKernelIdleTimer);
+    VSyncReactor(PhysicalDisplayId, std::unique_ptr<Clock> clock, VSyncTracker& tracker,
+                 size_t pendingFenceLimit, bool supportKernelIdleTimer);
     ~VSyncReactor();
 
     bool addPresentFence(std::shared_ptr<FenceTime>) final;
     void setIgnorePresentFences(bool ignore) final;
 
-    void startPeriodTransition(nsecs_t period) final;
+    void startPeriodTransition(nsecs_t period, bool force) final;
 
     bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                              bool* periodFlushed) final;
@@ -61,6 +62,7 @@
     bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
             REQUIRES(mMutex);
 
+    const PhysicalDisplayId mId;
     std::unique_ptr<Clock> const mClock;
     VSyncTracker& mTracker;
     size_t const mPendingLimit;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 76315d2..bc0e3bc 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -79,6 +79,18 @@
      */
     virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
 
+    /*
+     * Sets a render rate on the tracker. If the render rate is not a divisor
+     * of the period, the render rate is ignored until the period changes.
+     * The tracker will continue to track the vsync timeline and expect it
+     * to match the current period, however, nextAnticipatedVSyncTimeFrom will
+     * return vsyncs according to the render rate set. Setting a render rate is useful
+     * when a display is running at 120Hz but the render frame rate is 60Hz.
+     *
+     * \param [in] Fps   The render rate the tracker should operate at.
+     */
+    virtual void setRenderRate(Fps) = 0;
+
     virtual void dump(std::string& result) const = 0;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index ff31651..6ae10f3 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -42,12 +42,12 @@
 
 VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
 
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
     std::lock_guard lock(mLock);
     return getConfigsForRefreshRateLocked(fps);
 }
 
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
     if (const auto offsets = mOffsetsCache.get(fps)) {
         return offsets->get();
     }
@@ -134,7 +134,7 @@
         mThresholdForNextVsync(thresholdForNextVsync),
         mHwcMinWorkDuration(hwcMinWorkDuration) {}
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
     if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
         return getHighFpsOffsets(vsyncDuration);
     } else {
@@ -158,7 +158,7 @@
 }
 } // namespace
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
     const auto earlySfOffset =
             mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
 
@@ -196,7 +196,7 @@
     };
 }
 
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
     const auto earlySfOffset =
             mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
             ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
@@ -286,7 +286,7 @@
 }
 } // namespace
 
-WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
     const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
         return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
                               : std::chrono::nanoseconds(duration);
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 02ebd70..a24e43f 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -20,12 +20,12 @@
 #include <optional>
 #include <string>
 
+#include <android-base/thread_annotations.h>
 #include <ftl/small_map.h>
 #include <utils/Timers.h>
 
 #include <scheduler/Fps.h>
-
-#include "VsyncModulator.h"
+#include <scheduler/VsyncConfig.h>
 
 namespace android::scheduler {
 
@@ -37,8 +37,6 @@
  */
 class VsyncConfiguration {
 public:
-    using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
-
     virtual ~VsyncConfiguration() = default;
     virtual VsyncConfigSet getCurrentConfigs() const = 0;
     virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
@@ -85,7 +83,7 @@
     void dump(std::string& result) const override;
 
 protected:
-    virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+    virtual VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
 
     VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
 
@@ -115,7 +113,7 @@
                  nsecs_t hwcMinWorkDuration);
 
 private:
-    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+    VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
 
     VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
     VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
@@ -154,7 +152,7 @@
                  nsecs_t hwcMinWorkDuration);
 
 private:
-    VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+    VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
 
     const nsecs_t mSfDuration;
     const nsecs_t mAppDuration;
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 726a420..9177899 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -63,8 +63,9 @@
      * itself. The controller will end the period transition internally.
      *
      * \param [in] period   The period that the system is changing into.
+     * \param [in] force    True to recalibrate even if period matches the existing period.
      */
-    virtual void startPeriodTransition(nsecs_t period) = 0;
+    virtual void startPeriodTransition(nsecs_t period, bool force) = 0;
 
     /*
      * Tells the tracker to stop using present fences to get a vsync signal.
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index be57b2a..586357f 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -40,7 +40,7 @@
         mNow(now),
         mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
 
-VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
     std::lock_guard<std::mutex> lock(mMutex);
     mVsyncConfigSet = config;
     return updateVsyncConfigLocked();
@@ -53,14 +53,14 @@
         case Schedule::EarlyStart:
             if (token) {
                 mEarlyWakeupRequests.emplace(token);
-                token->linkToDeath(this);
+                token->linkToDeath(sp<DeathRecipient>::fromExisting(this));
             } else {
                 ALOGW("%s: EarlyStart requested without a valid token", __func__);
             }
             break;
         case Schedule::EarlyEnd: {
             if (token && mEarlyWakeupRequests.erase(token) > 0) {
-                token->unlinkToDeath(this);
+                token->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
             } else {
                 ALOGW("%s: Unexpected EarlyEnd", __func__);
             }
@@ -129,7 +129,7 @@
     return updateVsyncConfig();
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+VsyncConfig VsyncModulator::getVsyncConfig() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mVsyncConfig;
 }
@@ -147,7 +147,7 @@
     }
 }
 
-const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+const VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
     switch (getNextVsyncConfigType()) {
         case VsyncConfigType::Early:
             return mVsyncConfigSet.early;
@@ -158,12 +158,12 @@
     }
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+VsyncConfig VsyncModulator::updateVsyncConfig() {
     std::lock_guard<std::mutex> lock(mMutex);
     return updateVsyncConfigLocked();
 }
 
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
     const VsyncConfig& offsets = getNextVsyncConfig();
     mVsyncConfig = offsets;
 
@@ -187,9 +187,9 @@
     static_cast<void>(updateVsyncConfigLocked());
 }
 
-bool VsyncModulator::isVsyncConfigDefault() const {
+bool VsyncModulator::isVsyncConfigEarly() const {
     std::lock_guard<std::mutex> lock(mMutex);
-    return getNextVsyncConfigType() == VsyncConfigType::Late;
+    return getNextVsyncConfigType() != VsyncConfigType::Late;
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 537cae1..be0d334 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,19 +25,13 @@
 #include <binder/IBinder.h>
 #include <utils/Timers.h>
 
+#include <scheduler/TransactionSchedule.h>
+#include <scheduler/VsyncConfig.h>
+
 #include "../WpHash.h"
 
 namespace android::scheduler {
 
-// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
-// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
-// fixed number of frames, respectively.
-enum class TransactionSchedule {
-    Late,  // Default.
-    EarlyStart,
-    EarlyEnd
-};
-
 // Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
 class VsyncModulator : public IBinder::DeathRecipient {
 public:
@@ -51,47 +45,20 @@
     // This may keep early offsets for an extra frame, but avoids a race with transaction commit.
     static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
 
-    // Phase offsets and work durations for SF and app deadlines from VSYNC.
-    struct VsyncConfig {
-        nsecs_t sfOffset;
-        nsecs_t appOffset;
-        std::chrono::nanoseconds sfWorkDuration;
-        std::chrono::nanoseconds appWorkDuration;
-
-        bool operator==(const VsyncConfig& other) const {
-            return sfOffset == other.sfOffset && appOffset == other.appOffset &&
-                    sfWorkDuration == other.sfWorkDuration &&
-                    appWorkDuration == other.appWorkDuration;
-        }
-
-        bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
-    };
-
     using VsyncConfigOpt = std::optional<VsyncConfig>;
 
-    struct VsyncConfigSet {
-        VsyncConfig early;    // Used for early transactions, and during refresh rate change.
-        VsyncConfig earlyGpu; // Used during GPU composition.
-        VsyncConfig late;     // Default.
-        std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the
-                                                     // earliest present time
-
-        bool operator==(const VsyncConfigSet& other) const {
-            return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
-                    hwcMinWorkDuration == other.hwcMinWorkDuration;
-        }
-
-        bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
-    };
-
     using Clock = std::chrono::steady_clock;
     using TimePoint = Clock::time_point;
     using Now = TimePoint (*)();
 
     explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now);
 
+    bool isVsyncConfigEarly() const EXCLUDES(mMutex);
+
     VsyncConfig getVsyncConfig() const EXCLUDES(mMutex);
 
+    void cancelRefreshRateChange() { mRefreshRateChangePending = false; }
+
     [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex);
 
     // Changes offsets in response to transaction flags or commit.
@@ -109,8 +76,6 @@
 
     [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition);
 
-    [[nodiscard]] bool isVsyncConfigDefault() const;
-
 protected:
     // Called from unit tests as well
     void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 3a918a1..84671ae 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -16,11 +16,14 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <ftl/fake_guard.h>
 #include <scheduler/Fps.h>
 #include <scheduler/Timer.h>
 
 #include "VsyncSchedule.h"
 
+#include "ISchedulerCallback.h"
+#include "Utils/Dumper.h"
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
@@ -39,8 +42,8 @@
     }
 
 public:
-    explicit PredictedVsyncTracer(VsyncDispatch& dispatch)
-          : mRegistration(dispatch, makeVsyncCallback(), __func__) {
+    explicit PredictedVsyncTracer(std::shared_ptr<VsyncDispatch> dispatch)
+          : mRegistration(std::move(dispatch), makeVsyncCallback(), __func__) {
         schedule();
     }
 
@@ -51,24 +54,43 @@
     VSyncCallbackRegistration mRegistration;
 };
 
-VsyncSchedule::VsyncSchedule(FeatureFlags features)
-      : mTracker(createTracker()),
-        mDispatch(createDispatch(*mTracker)),
-        mController(createController(*mTracker, features)) {
-    if (features.test(Feature::kTracePredictedVsync)) {
-        mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch);
-    }
-}
+VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features)
+      : mId(id),
+        mTracker(createTracker(id)),
+        mDispatch(createDispatch(mTracker)),
+        mController(createController(id, *mTracker, features)),
+        mTracer(features.test(Feature::kTracePredictedVsync)
+                        ? std::make_unique<PredictedVsyncTracer>(mDispatch)
+                        : nullptr) {}
 
-VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller)
-      : mTracker(std::move(tracker)),
+VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, TrackerPtr tracker, DispatchPtr dispatch,
+                             ControllerPtr controller)
+      : mId(id),
+        mTracker(std::move(tracker)),
         mDispatch(std::move(dispatch)),
         mController(std::move(controller)) {}
 
-VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
 VsyncSchedule::~VsyncSchedule() = default;
 
+Period VsyncSchedule::period() const {
+    return Period::fromNs(mTracker->currentPeriod());
+}
+
+TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const {
+    return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns()));
+}
+
 void VsyncSchedule::dump(std::string& out) const {
+    utils::Dumper dumper(out);
+    {
+        std::lock_guard<std::mutex> lock(mHwVsyncLock);
+        dumper.dump("hwVsyncState", ftl::enum_string(mHwVsyncState));
+
+        ftl::FakeGuard guard(kMainThreadContext);
+        dumper.dump("pendingHwVsyncState", ftl::enum_string(mPendingHwVsyncState));
+        dumper.eol();
+    }
+
     out.append("VsyncController:\n");
     mController->dump(out);
 
@@ -76,40 +98,110 @@
     mDispatch->dump(out);
 }
 
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() {
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) {
     // TODO(b/144707443): Tune constants.
     constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
     constexpr size_t kHistorySize = 20;
     constexpr size_t kMinSamplesForPrediction = 6;
     constexpr uint32_t kDiscardOutlierPercent = 20;
 
-    return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction,
-                                            kDiscardOutlierPercent);
+    return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
+                                            kMinSamplesForPrediction, kDiscardOutlierPercent);
 }
 
-VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
+VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
     using namespace std::chrono_literals;
 
     // TODO(b/144707443): Tune constants.
     constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
     constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
 
-    return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+    return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), std::move(tracker),
                                                      kGroupDispatchWithin.count(),
                                                      kSnapToSameVsyncWithin.count());
 }
 
-VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker,
+VsyncSchedule::ControllerPtr VsyncSchedule::createController(PhysicalDisplayId id,
+                                                             VsyncTracker& tracker,
                                                              FeatureFlags features) {
     // TODO(b/144707443): Tune constants.
     constexpr size_t kMaxPendingFences = 20;
     const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer);
 
-    auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker,
+    auto reactor = std::make_unique<VSyncReactor>(id, std::make_unique<SystemClock>(), tracker,
                                                   kMaxPendingFences, hasKernelIdleTimer);
 
     reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences));
     return reactor;
 }
 
+void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period, bool force) {
+    std::lock_guard<std::mutex> lock(mHwVsyncLock);
+    mController->startPeriodTransition(period.ns(), force);
+    enableHardwareVsyncLocked(callback);
+}
+
+bool VsyncSchedule::addResyncSample(ISchedulerCallback& callback, TimePoint timestamp,
+                                    ftl::Optional<Period> hwcVsyncPeriod) {
+    bool needsHwVsync = false;
+    bool periodFlushed = false;
+    {
+        std::lock_guard<std::mutex> lock(mHwVsyncLock);
+        if (mHwVsyncState == HwVsyncState::Enabled) {
+            needsHwVsync = mController->addHwVsyncTimestamp(timestamp.ns(),
+                                                            hwcVsyncPeriod.transform(&Period::ns),
+                                                            &periodFlushed);
+        }
+    }
+    if (needsHwVsync) {
+        enableHardwareVsync(callback);
+    } else {
+        disableHardwareVsync(callback, false /* disallow */);
+    }
+    return periodFlushed;
+}
+
+void VsyncSchedule::enableHardwareVsync(ISchedulerCallback& callback) {
+    std::lock_guard<std::mutex> lock(mHwVsyncLock);
+    enableHardwareVsyncLocked(callback);
+}
+
+void VsyncSchedule::enableHardwareVsyncLocked(ISchedulerCallback& callback) {
+    if (mHwVsyncState == HwVsyncState::Disabled) {
+        getTracker().resetModel();
+        callback.setVsyncEnabled(mId, true);
+        mHwVsyncState = HwVsyncState::Enabled;
+    }
+}
+
+void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) {
+    std::lock_guard<std::mutex> lock(mHwVsyncLock);
+    switch (mHwVsyncState) {
+        case HwVsyncState::Enabled:
+            callback.setVsyncEnabled(mId, false);
+            [[fallthrough]];
+        case HwVsyncState::Disabled:
+            mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled;
+            break;
+        case HwVsyncState::Disallowed:
+            break;
+    }
+}
+
+bool VsyncSchedule::isHardwareVsyncAllowed(bool makeAllowed) {
+    std::lock_guard<std::mutex> lock(mHwVsyncLock);
+    if (makeAllowed && mHwVsyncState == HwVsyncState::Disallowed) {
+        mHwVsyncState = HwVsyncState::Disabled;
+    }
+    return mHwVsyncState != HwVsyncState::Disallowed;
+}
+
+void VsyncSchedule::setPendingHardwareVsyncState(bool enabled) {
+    mPendingHwVsyncState = enabled ? HwVsyncState::Enabled : HwVsyncState::Disabled;
+}
+
+bool VsyncSchedule::getPendingHardwareVsyncState() const {
+    return mPendingHwVsyncState == HwVsyncState::Enabled;
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 0d9b114..763d058 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -19,10 +19,27 @@
 #include <memory>
 #include <string>
 
+#include <ThreadContext.h>
+#include <android-base/thread_annotations.h>
+#include <ftl/enum.h>
+#include <ftl/optional.h>
 #include <scheduler/Features.h>
+#include <scheduler/Time.h>
+#include <ui/DisplayId.h>
+
+namespace android {
+class EventThreadTest;
+class VsyncScheduleTest;
+}
+
+namespace android::fuzz {
+class SchedulerFuzzer;
+}
 
 namespace android::scheduler {
 
+struct ISchedulerCallback;
+
 // TODO(b/185535769): Rename classes, and remove aliases.
 class VSyncDispatch;
 class VSyncTracker;
@@ -34,42 +51,106 @@
 // Schedule that synchronizes to hardware VSYNC of a physical display.
 class VsyncSchedule {
 public:
-    explicit VsyncSchedule(FeatureFlags);
-    VsyncSchedule(VsyncSchedule&&);
+    VsyncSchedule(PhysicalDisplayId, FeatureFlags);
     ~VsyncSchedule();
 
+    Period period() const;
+    TimePoint vsyncDeadlineAfter(TimePoint) const;
+
+    // Inform the schedule that the period is changing and the schedule needs to recalibrate
+    // itself. The schedule will end the period transition internally. This will
+    // enable hardware VSYNCs in order to calibrate.
+    //
+    // \param [in] period   The period that the system is changing into.
+    // \param [in] force    True to force a transition even if it is not a
+    //                      change.
+    void startPeriodTransition(ISchedulerCallback&, Period period, bool force);
+
+    // Pass a VSYNC sample to VsyncController. Return true if
+    // VsyncController detected that the VSYNC period changed. Enable or disable
+    // hardware VSYNCs depending on whether more samples are needed.
+    bool addResyncSample(ISchedulerCallback&, TimePoint timestamp,
+                         ftl::Optional<Period> hwcVsyncPeriod);
+
     // TODO(b/185535769): Hide behind API.
     const VsyncTracker& getTracker() const { return *mTracker; }
     VsyncTracker& getTracker() { return *mTracker; }
     VsyncController& getController() { return *mController; }
 
+    // TODO(b/185535769): Once these are hidden behind the API, they may no
+    // longer need to be shared_ptrs.
+    using DispatchPtr = std::shared_ptr<VsyncDispatch>;
+    using TrackerPtr = std::shared_ptr<VsyncTracker>;
+
     // TODO(b/185535769): Remove once VsyncSchedule owns all registrations.
-    VsyncDispatch& getDispatch() { return *mDispatch; }
+    DispatchPtr getDispatch() { return mDispatch; }
 
     void dump(std::string&) const;
 
-private:
-    friend class TestableScheduler;
+    // Turn on hardware VSYNCs, unless mHwVsyncState is Disallowed, in which
+    // case this call is ignored.
+    void enableHardwareVsync(ISchedulerCallback&) EXCLUDES(mHwVsyncLock);
 
-    using TrackerPtr = std::unique_ptr<VsyncTracker>;
-    using DispatchPtr = std::unique_ptr<VsyncDispatch>;
+    // Disable hardware VSYNCs. If `disallow` is true, future calls to
+    // enableHardwareVsync are ineffective until isHardwareVsyncAllowed is
+    // called with `makeAllowed` set to true.
+    void disableHardwareVsync(ISchedulerCallback&, bool disallow) EXCLUDES(mHwVsyncLock);
+
+    // If true, enableHardwareVsync can enable hardware VSYNC (if not already
+    // enabled). If false, enableHardwareVsync does nothing.
+    bool isHardwareVsyncAllowed(bool makeAllowed) EXCLUDES(mHwVsyncLock);
+
+    void setPendingHardwareVsyncState(bool enabled) REQUIRES(kMainThreadContext);
+
+    bool getPendingHardwareVsyncState() const REQUIRES(kMainThreadContext);
+
+protected:
     using ControllerPtr = std::unique_ptr<VsyncController>;
 
     // For tests.
-    VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr);
+    VsyncSchedule(PhysicalDisplayId, TrackerPtr, DispatchPtr, ControllerPtr);
 
-    static TrackerPtr createTracker();
-    static DispatchPtr createDispatch(VsyncTracker&);
-    static ControllerPtr createController(VsyncTracker&, FeatureFlags);
+private:
+    friend class TestableScheduler;
+    friend class android::EventThreadTest;
+    friend class android::VsyncScheduleTest;
+    friend class android::fuzz::SchedulerFuzzer;
+
+    static TrackerPtr createTracker(PhysicalDisplayId);
+    static DispatchPtr createDispatch(TrackerPtr);
+    static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);
+
+    void enableHardwareVsyncLocked(ISchedulerCallback&) REQUIRES(mHwVsyncLock);
+
+    mutable std::mutex mHwVsyncLock;
+    enum class HwVsyncState {
+        // Hardware VSYNCs are currently enabled.
+        Enabled,
+
+        // Hardware VSYNCs are currently disabled. They can be enabled by a call
+        // to `enableHardwareVsync`.
+        Disabled,
+
+        // Hardware VSYNCs are not currently allowed (e.g. because the display
+        // is off).
+        Disallowed,
+
+        ftl_last = Disallowed,
+    };
+    HwVsyncState mHwVsyncState GUARDED_BY(mHwVsyncLock) = HwVsyncState::Disallowed;
+
+    // Pending state, in case an attempt is made to set the state while the
+    // device is off.
+    HwVsyncState mPendingHwVsyncState GUARDED_BY(kMainThreadContext) = HwVsyncState::Disabled;
 
     class PredictedVsyncTracer;
     using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
 
-    // Effectively const except in move constructor.
-    TrackerPtr mTracker;
-    DispatchPtr mDispatch;
-    ControllerPtr mController;
-    TracerPtr mTracer;
+    const PhysicalDisplayId mId;
+    const TrackerPtr mTracker;
+    const DispatchPtr mDispatch;
+    const ControllerPtr mController;
+    const TracerPtr mTracer;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..d6329e2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -23,7 +23,7 @@
 #include <type_traits>
 
 #include <android-base/stringprintf.h>
-#include <utils/Timers.h>
+#include <scheduler/Time.h>
 
 namespace android {
 
@@ -52,6 +52,7 @@
     constexpr float getValue() const { return mFrequency; }
     int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }
 
+    constexpr Period getPeriod() const { return Period::fromNs(mPeriod); }
     constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }
 
 private:
@@ -66,6 +67,18 @@
     Fps max = Fps::fromValue(std::numeric_limits<float>::max());
 
     bool includes(Fps) const;
+    bool includes(FpsRange) const;
+};
+
+struct FpsRanges {
+    // The range of refresh rates that refers to the display mode setting.
+    FpsRange physical;
+
+    // the range of frame rates that refers to the render rate, which is
+    // the rate that frames are swapped.
+    FpsRange render;
+
+    bool valid() const;
 };
 
 static_assert(std::is_trivially_copyable_v<Fps>);
@@ -127,13 +140,39 @@
     return !(lhs == rhs);
 }
 
+inline bool operator==(const FpsRanges& lhs, const FpsRanges& rhs) {
+    return lhs.physical == rhs.physical && lhs.render == rhs.render;
+}
+
+inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) {
+    return !(lhs == rhs);
+}
+
+inline unsigned operator/(Fps lhs, Fps rhs) {
+    return static_cast<unsigned>(std::ceil(lhs.getValue() / rhs.getValue()));
+}
+
 } // namespace fps_approx_ops
 
+constexpr Fps operator/(Fps fps, unsigned divisor) {
+    return Fps::fromPeriodNsecs(fps.getPeriodNsecs() * static_cast<nsecs_t>(divisor));
+}
+
 inline bool FpsRange::includes(Fps fps) const {
     using fps_approx_ops::operator<=;
     return min <= fps && fps <= max;
 }
 
+inline bool FpsRange::includes(FpsRange range) const {
+    using namespace fps_approx_ops;
+    return min <= range.min && max >= range.max;
+}
+
+inline bool FpsRanges::valid() const {
+    using fps_approx_ops::operator>=;
+    return physical.max >= render.max;
+}
+
 struct FpsApproxEqual {
     bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
 };
@@ -151,4 +190,10 @@
     return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str());
 }
 
+inline std::string to_string(FpsRanges ranges) {
+    const auto& [physical, render] = ranges;
+    return base::StringPrintf("{physical=%s, render=%s}", to_string(physical).c_str(),
+                              to_string(render).c_str());
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
new file mode 100644
index 0000000..db38ebe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <ftl/non_null.h>
+#include <scheduler/Fps.h>
+
+// TODO(b/241285191): Pull this to <ui/DisplayMode.h>
+#include "DisplayHardware/DisplayMode.h"
+
+namespace android::scheduler {
+
+struct FrameRateMode {
+    Fps fps; // The render frame rate, which is a divisor of modePtr->getFps().
+    ftl::NonNull<DisplayModePtr> modePtr;
+
+    bool operator==(const FrameRateMode& other) const {
+        return isApproxEqual(fps, other.fps) && modePtr == other.modePtr;
+    }
+
+    bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
+};
+
+inline std::string to_string(const FrameRateMode& mode) {
+    return to_string(mode.fps) + " (" + to_string(mode.modePtr->getFps()) + ")";
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h b/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h
new file mode 100644
index 0000000..23ae83f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/PresentLatencyTracker.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <memory>
+#include <queue>
+#include <utility>
+
+#include <scheduler/Time.h>
+
+namespace android {
+
+class FenceTime;
+
+namespace scheduler {
+
+// Computes composite-to-present latency by tracking recently composited frames pending to present.
+class PresentLatencyTracker {
+public:
+    // For tests.
+    static constexpr size_t kMaxPendingFrames = 4;
+
+    // Returns the present latency of the latest frame.
+    Duration trackPendingFrame(TimePoint compositeTime,
+                               std::shared_ptr<FenceTime> presentFenceTime);
+
+private:
+    struct PendingFrame {
+        PendingFrame(TimePoint compositeTime, std::shared_ptr<FenceTime> presentFenceTime)
+              : compositeTime(compositeTime), presentFenceTime(std::move(presentFenceTime)) {}
+
+        const TimePoint compositeTime;
+        const std::shared_ptr<FenceTime> presentFenceTime;
+    };
+
+    std::queue<PendingFrame> mPendingFrames;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Time.h b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
new file mode 100644
index 0000000..ba1459a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Time.h
@@ -0,0 +1,88 @@
+/*
+ * 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 <chrono>
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace scheduler {
+
+// TODO(b/185535769): Pull Clock.h to libscheduler to reuse this.
+using SchedulerClock = std::chrono::steady_clock;
+static_assert(SchedulerClock::is_steady);
+
+} // namespace scheduler
+
+struct Duration;
+
+struct TimePoint : scheduler::SchedulerClock::time_point {
+    constexpr TimePoint() = default;
+    explicit constexpr TimePoint(const Duration&);
+
+    // Implicit conversion from std::chrono counterpart.
+    constexpr TimePoint(scheduler::SchedulerClock::time_point p)
+          : scheduler::SchedulerClock::time_point(p) {}
+
+    static constexpr TimePoint fromNs(nsecs_t);
+
+    static TimePoint now() { return scheduler::SchedulerClock::now(); };
+
+    nsecs_t ns() const;
+};
+
+struct Duration : TimePoint::duration {
+    // Implicit conversion from std::chrono counterpart.
+    template <typename R, typename P>
+    constexpr Duration(std::chrono::duration<R, P> d) : TimePoint::duration(d) {}
+
+    static constexpr Duration fromNs(nsecs_t ns) { return {std::chrono::nanoseconds(ns)}; }
+
+    nsecs_t ns() const { return std::chrono::nanoseconds(*this).count(); }
+};
+
+using Period = Duration;
+
+constexpr TimePoint::TimePoint(const Duration& d) : scheduler::SchedulerClock::time_point(d) {}
+
+constexpr TimePoint TimePoint::fromNs(nsecs_t ns) {
+    return TimePoint(Duration::fromNs(ns));
+}
+
+inline nsecs_t TimePoint::ns() const {
+    return Duration(time_since_epoch()).ns();
+}
+
+// Shorthand to convert the tick count of a Duration to Period and Rep. For example:
+//
+//     const auto i = ticks<std::ratio<1>>(d);      // Integer seconds.
+//     const auto f = ticks<std::milli, float>(d);  // Floating-point milliseconds.
+//
+template <typename Period, typename Rep = Duration::rep>
+constexpr Rep ticks(Duration d) {
+    using D = std::chrono::duration<Rep, Period>;
+    return std::chrono::duration_cast<D>(d).count();
+}
+
+inline std::string to_string(Duration d) {
+    return base::StringPrintf("%.3f ms", ticks<std::milli, float>(d));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
new file mode 100644
index 0000000..6fc44dd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 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
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+    Late, // Default.
+    EarlyStart,
+    EarlyEnd
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
new file mode 100644
index 0000000..47d95a8
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 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 <chrono>
+
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+using namespace std::chrono_literals;
+
+// Phase offsets and work durations for SF and app deadlines from VSYNC.
+struct VsyncConfig {
+    nsecs_t sfOffset;
+    nsecs_t appOffset;
+    std::chrono::nanoseconds sfWorkDuration;
+    std::chrono::nanoseconds appWorkDuration;
+
+    bool operator==(const VsyncConfig& other) const {
+        return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+                sfWorkDuration == other.sfWorkDuration && appWorkDuration == other.appWorkDuration;
+    }
+
+    bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+
+    // The duration for which SF can delay a frame if it is considered early based on the
+    // VsyncConfig::appWorkDuration.
+    static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
+};
+
+struct VsyncConfigSet {
+    VsyncConfig early;    // Used for early transactions, and during refresh rate change.
+    VsyncConfig earlyGpu; // Used during GPU composition.
+    VsyncConfig late;     // Default.
+    std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the earliest present time.
+
+    bool operator==(const VsyncConfigSet& other) const {
+        return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
+                hwcMinWorkDuration == other.hwcMinWorkDuration;
+    }
+
+    bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h
new file mode 100644
index 0000000..c64a3cd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncId.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <cstdint>
+
+namespace android {
+
+// TODO(b/185536303): Import StrongTyping.h into FTL so it can be used here.
+
+// Sequential frame identifier, also known as FrameTimeline token.
+struct VsyncId {
+    int64_t value = -1;
+};
+
+inline bool operator==(VsyncId lhs, VsyncId rhs) {
+    return lhs.value == rhs.value;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
new file mode 100644
index 0000000..3d0f1a9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/CompositionCoverage.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 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 <cstdint>
+
+#include <ftl/flags.h>
+
+namespace android {
+
+// Whether composition was covered by HWC and/or GPU.
+enum class CompositionCoverage : std::uint8_t {
+    Hwc = 1 << 0,
+
+    // Mutually exclusive: The composition either used the GPU, or reused a buffer that had been
+    // composited on the GPU.
+    Gpu = 1 << 1,
+    GpuReuse = 1 << 2,
+};
+
+using CompositionCoverageFlags = ftl::Flags<CompositionCoverage>;
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
new file mode 100644
index 0000000..cc41925
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 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 <scheduler/Time.h>
+#include <scheduler/VsyncId.h>
+
+namespace android {
+
+struct ICompositor {
+    // Configures physical displays, processing hotplug and/or mode setting via the Composer HAL.
+    virtual void configure() = 0;
+
+    // Commits transactions for layers and displays. Returns whether any state has been invalidated,
+    // i.e. whether a frame should be composited for each display.
+    virtual bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) = 0;
+
+    // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
+    // via RenderEngine and the Composer HAL, respectively.
+    virtual void composite(TimePoint frameTime, VsyncId) = 0;
+
+    // Samples the composited frame via RegionSamplingThread.
+    virtual void sample() = 0;
+
+protected:
+    ~ICompositor() = default;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp b/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp
new file mode 100644
index 0000000..8f3e081
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/src/PresentLatencyTracker.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 <scheduler/PresentLatencyTracker.h>
+
+#include <cutils/compiler.h>
+#include <log/log.h>
+#include <ui/FenceTime.h>
+
+namespace android::scheduler {
+
+Duration PresentLatencyTracker::trackPendingFrame(TimePoint compositeTime,
+                                                  std::shared_ptr<FenceTime> presentFenceTime) {
+    Duration presentLatency = Duration::zero();
+    while (!mPendingFrames.empty()) {
+        const auto& pendingFrame = mPendingFrames.front();
+        const auto presentTime =
+                TimePoint::fromNs(pendingFrame.presentFenceTime->getCachedSignalTime());
+
+        if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_PENDING)) {
+            break;
+        }
+
+        if (presentTime == TimePoint::fromNs(Fence::SIGNAL_TIME_INVALID)) {
+            ALOGE("%s: Invalid present fence", __func__);
+        } else {
+            presentLatency = presentTime - pendingFrame.compositeTime;
+        }
+
+        mPendingFrames.pop();
+    }
+
+    mPendingFrames.emplace(compositeTime, std::move(presentFenceTime));
+
+    if (CC_UNLIKELY(mPendingFrames.size() > kMaxPendingFrames)) {
+        ALOGE("%s: Too many pending frames", __func__);
+        mPendingFrames.pop();
+    }
+
+    return presentLatency;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
new file mode 100644
index 0000000..8952ca9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/tests/PresentLatencyTrackerTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <algorithm>
+#include <array>
+
+#include <scheduler/PresentLatencyTracker.h>
+#include <ui/FenceTime.h>
+
+namespace android::scheduler {
+namespace {
+
+using FencePair = std::pair<sp<Fence>, std::shared_ptr<FenceTime>>;
+
+FencePair makePendingFence(FenceToFenceTimeMap& fenceMap) {
+    const auto fence = sp<Fence>::make();
+    return {fence, fenceMap.createFenceTimeForTest(fence)};
+}
+
+} // namespace
+
+TEST(PresentLatencyTrackerTest, skipsInvalidFences) {
+    PresentLatencyTracker tracker;
+
+    const TimePoint kCompositeTime = TimePoint::fromNs(999);
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE), Duration::zero());
+
+    FenceToFenceTimeMap fenceMap;
+    const auto [fence, fenceTime] = makePendingFence(fenceMap);
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
+
+    fenceTime->signalForTest(9999);
+
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, FenceTime::NO_FENCE),
+              Duration::fromNs(9000));
+}
+
+TEST(PresentLatencyTrackerTest, tracksPendingFrames) {
+    PresentLatencyTracker tracker;
+
+    FenceToFenceTimeMap fenceMap;
+    std::array<FencePair, PresentLatencyTracker::kMaxPendingFrames> fences;
+    std::generate(fences.begin(), fences.end(), [&fenceMap] { return makePendingFence(fenceMap); });
+
+    // The present latency is 0 if all fences are pending.
+    const TimePoint kCompositeTime = TimePoint::fromNs(1234);
+    for (const auto& [fence, fenceTime] : fences) {
+        EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fenceTime), Duration::zero());
+    }
+
+    // If multiple frames have been presented...
+    constexpr size_t kPresentCount = fences.size() / 2;
+    for (size_t i = 0; i < kPresentCount; i++) {
+        fences[i].second->signalForTest(kCompositeTime.ns() + static_cast<nsecs_t>(i));
+    }
+
+    const auto fence = makePendingFence(fenceMap);
+
+    // ...then the present latency is measured using the latest frame.
+    constexpr Duration kPresentLatency = Duration::fromNs(static_cast<nsecs_t>(kPresentCount) - 1);
+    EXPECT_EQ(tracker.trackPendingFrame(kCompositeTime, fence.second), kPresentLatency);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
new file mode 100644
index 0000000..09dac23
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "ScreenCaptureOutput.h"
+#include "ScreenCaptureRenderSurface.h"
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
+    std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
+            ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
+            const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine,
+                                                                  args.renderArea,
+                                                                  args.colorProfile,
+                                                                  args.regionSampling);
+    output->editState().isSecure = args.renderArea.isSecure();
+    output->setCompositionEnabled(true);
+    output->setLayerFilter({args.layerStack});
+    output->setRenderSurface(std::make_unique<ScreenCaptureRenderSurface>(std::move(args.buffer)));
+    output->setDisplayBrightness(args.sdrWhitePointNits, args.displayBrightnessNits);
+    output->editState().clientTargetBrightness = args.targetBrightness;
+
+    output->setDisplayColorProfile(std::make_unique<compositionengine::impl::DisplayColorProfile>(
+            compositionengine::DisplayColorProfileCreationArgsBuilder()
+                    .setHasWideColorGamut(true)
+                    .Build()));
+
+    const Rect& sourceCrop = args.renderArea.getSourceCrop();
+    const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags());
+    const Rect orientedDisplaySpaceRect{args.renderArea.getReqWidth(),
+                                        args.renderArea.getReqHeight()};
+    output->setProjection(orientation, sourceCrop, orientedDisplaySpaceRect);
+    output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()});
+
+    {
+        std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput";
+        if (auto displayDevice = args.renderArea.getDisplayDevice()) {
+            base::StringAppendF(&name, " for %" PRIu64, displayDevice->getId().value);
+        }
+        output->setName(name);
+    }
+    return output;
+}
+
+ScreenCaptureOutput::ScreenCaptureOutput(
+        const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
+        bool regionSampling)
+      : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling) {}
+
+void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
+    auto& outputState = editState();
+    outputState.dataspace = mColorProfile.dataspace;
+    outputState.renderIntent = mColorProfile.renderIntent;
+}
+
+renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisplaySettings()
+        const {
+    auto clientCompositionDisplay =
+            compositionengine::impl::Output::generateClientCompositionDisplaySettings();
+    clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
+    return clientCompositionDisplay;
+}
+
+std::vector<compositionengine::LayerFE::LayerSettings>
+ScreenCaptureOutput::generateClientCompositionRequests(
+        bool supportsProtectedContent, ui::Dataspace outputDataspace,
+        std::vector<compositionengine::LayerFE*>& outLayerFEs) {
+    auto clientCompositionLayers = compositionengine::impl::Output::
+            generateClientCompositionRequests(supportsProtectedContent, outputDataspace,
+                                              outLayerFEs);
+
+    if (mRegionSampling) {
+        for (auto& layer : clientCompositionLayers) {
+            layer.backgroundBlurRadius = 0;
+            layer.blurRegions.clear();
+        }
+    }
+
+    if (outputDataspace == ui::Dataspace::BT2020_HLG) {
+        for (auto& layer : clientCompositionLayers) {
+            auto transfer = layer.sourceDataspace & ui::Dataspace::TRANSFER_MASK;
+            if (transfer != static_cast<int32_t>(ui::Dataspace::TRANSFER_HLG) &&
+                transfer != static_cast<int32_t>(ui::Dataspace::TRANSFER_ST2084)) {
+                layer.whitePointNits *= (1000.0f / 203.0f);
+            }
+        }
+    }
+
+    Rect sourceCrop = mRenderArea.getSourceCrop();
+    compositionengine::LayerFE::LayerSettings fillLayer;
+    fillLayer.source.buffer.buffer = nullptr;
+    fillLayer.source.solidColor = half3(0.0f, 0.0f, 0.0f);
+    fillLayer.geometry.boundaries =
+            FloatRect(static_cast<float>(sourceCrop.left), static_cast<float>(sourceCrop.top),
+                      static_cast<float>(sourceCrop.right), static_cast<float>(sourceCrop.bottom));
+    fillLayer.alpha = half(RenderArea::getCaptureFillValue(mRenderArea.getCaptureFill()));
+    clientCompositionLayers.insert(clientCompositionLayers.begin(), fillLayer);
+
+    return clientCompositionLayers;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
new file mode 100644
index 0000000..3c307b0
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/Rect.h>
+
+#include "RenderArea.h"
+
+namespace android {
+
+struct ScreenCaptureOutputArgs {
+    const compositionengine::CompositionEngine& compositionEngine;
+    const compositionengine::Output::ColorProfile& colorProfile;
+    const RenderArea& renderArea;
+    ui::LayerStack layerStack;
+    std::shared_ptr<renderengine::ExternalTexture> buffer;
+    float sdrWhitePointNits;
+    float displayBrightnessNits;
+    // Counterintuitively, when targetBrightness > 1.0 then dim the scene.
+    float targetBrightness;
+    bool regionSampling;
+};
+
+// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
+//
+// SurfaceFlinger passes instances of ScreenCaptureOutput to CompositionEngine in calls to
+// SurfaceFlinger::captureLayers and SurfaceFlinger::captureDisplay.
+class ScreenCaptureOutput : public compositionengine::impl::Output {
+public:
+    ScreenCaptureOutput(const RenderArea& renderArea,
+                        const compositionengine::Output::ColorProfile& colorProfile,
+                        bool regionSampling);
+
+    void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
+
+    std::vector<compositionengine::LayerFE::LayerSettings> generateClientCompositionRequests(
+            bool supportsProtectedContent, ui::Dataspace outputDataspace,
+            std::vector<compositionengine::LayerFE*>& outLayerFEs) override;
+
+protected:
+    bool getSkipColorTransform() const override { return false; }
+    renderengine::DisplaySettings generateClientCompositionDisplaySettings() const override;
+
+private:
+    const RenderArea& mRenderArea;
+    const compositionengine::Output::ColorProfile& mColorProfile;
+    const bool mRegionSampling;
+};
+
+std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
+
+} // namespace android
diff --git a/services/surfaceflinger/ScreenCaptureRenderSurface.h b/services/surfaceflinger/ScreenCaptureRenderSurface.h
new file mode 100644
index 0000000..2097300
--- /dev/null
+++ b/services/surfaceflinger/ScreenCaptureRenderSurface.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <memory>
+
+#include <compositionengine/RenderSurface.h>
+#include <renderengine/impl/ExternalTexture.h>
+#include <ui/Fence.h>
+#include <ui/Size.h>
+
+namespace android {
+
+// ScreenCaptureRenderSurface is a RenderSurface that returns a preallocated buffer used by
+// ScreenCaptureOutput.
+class ScreenCaptureRenderSurface : public compositionengine::RenderSurface {
+public:
+    ScreenCaptureRenderSurface(std::shared_ptr<renderengine::ExternalTexture> buffer)
+          : mBuffer(std::move(buffer)){};
+
+    std::shared_ptr<renderengine::ExternalTexture> dequeueBuffer(
+            base::unique_fd* /* bufferFence */) override {
+        return mBuffer;
+    }
+
+    void queueBuffer(base::unique_fd readyFence) override {
+        mRenderFence = sp<Fence>::make(readyFence.release());
+    }
+
+    const sp<Fence>& getClientTargetAcquireFence() const override { return mRenderFence; }
+
+    bool supportsCompositionStrategyPrediction() const override { return false; }
+
+    bool isValid() const override { return true; }
+
+    void initialize() override {}
+
+    const ui::Size& getSize() const override { return mSize; }
+
+    bool isProtected() const override { return mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED; }
+
+    void setDisplaySize(const ui::Size&) override {}
+
+    void setBufferDataspace(ui::Dataspace) override {}
+
+    void setBufferPixelFormat(ui::PixelFormat) override {}
+
+    void setProtected(bool /* useProtected */) override {}
+
+    status_t beginFrame(bool /* mustRecompose */) override { return OK; }
+
+    void prepareFrame(bool /* usesClientComposition */, bool /* usesDeviceComposition */) override {
+    }
+
+    void onPresentDisplayCompleted() override {}
+
+    void dump(std::string& /* result */) const override {}
+
+private:
+    std::shared_ptr<renderengine::ExternalTexture> mBuffer;
+
+    sp<Fence> mRenderFence = Fence::NO_FENCE;
+
+    ui::Size mSize;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 314b4cd..da4186b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -30,6 +30,7 @@
 #include <android-base/strings.h>
 #include <android/configuration.h>
 #include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/StaticDisplayInfo.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
@@ -43,18 +44,23 @@
 #include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayColorProfileCreationArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/DisplayColorProfile.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <configstore/Utils.h>
 #include <cutils/compiler.h>
 #include <cutils/properties.h>
+#include <ftl/algorithm.h>
+#include <ftl/concat.h>
 #include <ftl/fake_guard.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
+#include <ftl/unit.h>
+#include <gui/AidlStatusUtil.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <gui/IProducerListener.h>
@@ -81,6 +87,7 @@
 #include <ui/DisplayState.h>
 #include <ui/DynamicDisplayInfo.h>
 #include <ui/GraphicBufferAllocator.h>
+#include <ui/LayerStack.h>
 #include <ui/PixelFormat.h>
 #include <ui/StaticDisplayInfo.h>
 #include <utils/StopWatch.h>
@@ -98,17 +105,18 @@
 #include <memory>
 #include <mutex>
 #include <optional>
+#include <string>
 #include <type_traits>
 #include <unordered_map>
+#include <vector>
 
+#include <gui/LayerStatePermissions.h>
 #include <ui/DisplayIdentification.h>
 #include "BackgroundExecutor.h"
-#include "BufferLayer.h"
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
 #include "Client.h"
+#include "ClientCache.h"
 #include "Colorizer.h"
-#include "ContainerLayer.h"
+#include "Display/DisplayMap.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/FramebufferSurface.h"
@@ -117,33 +125,34 @@
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
 #include "FpsReporter.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshot.h"
 #include "HdrLayerInfoReporter.h"
 #include "Layer.h"
 #include "LayerProtoHelper.h"
 #include "LayerRenderArea.h"
 #include "LayerVector.h"
-#include "MonitoredProducer.h"
 #include "MutexUtils.h"
 #include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
-#include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
-#include "Scheduler/VsyncController.h"
+#include "Scheduler/VsyncModulator.h"
+#include "ScreenCaptureOutput.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
 #include "TunnelModeEnabledReporter.h"
+#include "Utils/Dumper.h"
 #include "WindowInfosListenerInvoker.h"
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
@@ -154,13 +163,21 @@
 #define NO_THREAD_SAFETY_ANALYSIS \
     _Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
 
+// To enable layer borders in the system, change the below flag to true.
+#undef DOES_CONTAIN_BORDER
+#define DOES_CONTAIN_BORDER false
+
 namespace android {
 
+using namespace std::chrono_literals;
 using namespace std::string_literals;
+using namespace std::string_view_literals;
 
 using namespace hardware::configstore;
 using namespace hardware::configstore::V1_0;
 using namespace sysprop;
+using ftl::Flags;
+using namespace ftl::flag_operators;
 
 using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
 using aidl::android::hardware::graphics::composer3::Capability;
@@ -169,47 +186,29 @@
         OutputCompositionState::CompositionStrategyPredictionState;
 
 using base::StringAppendF;
+using display::PhysicalDisplay;
+using display::PhysicalDisplays;
+using frontend::TransactionHandler;
 using gui::DisplayInfo;
+using gui::GameMode;
 using gui::IDisplayEventConnection;
 using gui::IWindowInfosListener;
+using gui::LayerMetadata;
 using gui::WindowInfo;
-using ui::ColorMode;
+using gui::aidl_utils::binderStatusFromStatusT;
+using scheduler::VsyncModulator;
 using ui::Dataspace;
 using ui::DisplayPrimaries;
 using ui::RenderIntent;
 
-using KernelIdleTimerController = scheduler::RefreshRateConfigs::KernelIdleTimerController;
+using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
 
 namespace hal = android::hardware::graphics::composer::hal;
 
 namespace {
 
-#pragma clang diagnostic push
-#pragma clang diagnostic error "-Wswitch-enum"
-
-bool isWideColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::ADOBE_RGB:
-        case ColorMode::DCI_P3:
-        case ColorMode::BT2020:
-        case ColorMode::DISPLAY_BT2020:
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-            return true;
-        case ColorMode::NATIVE:
-        case ColorMode::STANDARD_BT601_625:
-        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
-        case ColorMode::STANDARD_BT601_525:
-        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
-        case ColorMode::STANDARD_BT709:
-        case ColorMode::SRGB:
-            return false;
-    }
-    return false;
-}
-
-#pragma clang diagnostic pop
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
 
 // TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
 constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -258,6 +257,51 @@
     return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
 }
 
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+    using fps_approx_ops::operator>;
+    Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+    return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+            outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+                                     ui::DisplayMode& outMode) {
+    if (isAbove4k30(outMode) &&
+        std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                    [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+        for (ui::Hdr type : displayHdrTypes) {
+            if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+                outMode.supportedHdrTypes.push_back(type);
+            }
+        }
+    } else {
+        for (ui::Hdr type : displayHdrTypes) {
+            if (type != ui::Hdr::DOLBY_VISION_4K30) {
+                outMode.supportedHdrTypes.push_back(type);
+            }
+        }
+    }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+    std::vector<ui::Hdr> hdrTypes;
+    for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+        if (type != ui::Hdr::DOLBY_VISION_4K30) {
+            hdrTypes.push_back(type);
+        }
+    }
+    return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+            displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+            displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
+uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) {
+    if (!surfaceControl) {
+        return UNASSIGNED_LAYER_ID;
+    }
+    return LayerHandle::getLayerId(surfaceControl->getHandle());
+}
+
 }  // namespace anonymous
 
 // ---------------------------------------------------------------------------
@@ -270,9 +314,12 @@
 const String16 sDump("android.permission.DUMP");
 const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
 const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
+const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
 
 const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
+static const int MAX_TRACING_MEMORY = 1024 * 1024 * 1024; // 1GB
+
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -280,7 +327,6 @@
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 uint32_t SurfaceFlinger::maxGraphicsWidth;
 uint32_t SurfaceFlinger::maxGraphicsHeight;
-bool SurfaceFlinger::hasWideColorDisplay;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
@@ -302,37 +348,30 @@
     }
 }
 
-bool callingThreadHasRotateSurfaceFlingerAccess() {
+bool callingThreadHasPermission(const String16& permission) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
     const int uid = ipc->getCallingUid();
     return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
-            PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
+            PermissionCache::checkPermission(permission, pid, uid);
 }
 
-bool callingThreadHasInternalSystemWindowAccess() {
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int pid = ipc->getCallingPid();
-    const int uid = ipc->getCallingUid();
-    return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
-        PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
-}
+ui::Transform::RotationFlags SurfaceFlinger::sActiveDisplayRotationFlags = ui::Transform::ROT_0;
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
         mPid(getpid()),
-        mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(mFactory.createFrameTracer()),
         mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
-        mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
+        mTunnelModeEnabledReporter(sp<TunnelModeEnabledReporter>::make()),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)),
         mInternalDisplayDensity(
                 getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
         mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
-        mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make(*this)) {
+        mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
 }
 
@@ -350,11 +389,11 @@
     maxGraphicsWidth = std::max(max_graphics_width(0), 0);
     maxGraphicsHeight = std::max(max_graphics_height(0), 0);
 
-    hasWideColorDisplay = has_wide_color_display(false);
+    mSupportsWideColor = has_wide_color_display(false);
     mDefaultCompositionDataspace =
             static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
     mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
-            hasWideColorDisplay ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
+            mSupportsWideColor ? Dataspace::DISPLAY_P3 : Dataspace::V0_SRGB));
     defaultCompositionDataspace = mDefaultCompositionDataspace;
     wideColorGamutCompositionDataspace = mWideColorGamutCompositionDataspace;
     defaultCompositionPixelFormat = static_cast<ui::PixelFormat>(
@@ -378,32 +417,20 @@
     // debugging stuff...
     char value[PROPERTY_VALUE_MAX];
 
-    property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
-    mGpuToCpuSupported = !atoi(value);
-
     property_get("ro.build.type", value, "user");
     mIsUserBuild = strcmp(value, "user") == 0;
 
     mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u);
 
-    // DDMS debugging deprecated (b/120782499)
-    property_get("debug.sf.ddms", value, "0");
-    int debugDdms = atoi(value);
-    ALOGI_IF(debugDdms, "DDMS debugging not supported");
-
-    property_get("debug.sf.enable_gl_backpressure", value, "0");
-    mPropagateBackpressureClientComposition = atoi(value);
-    ALOGI_IF(mPropagateBackpressureClientComposition,
-             "Enabling backpressure propagation for Client Composition");
+    mBackpressureGpuComposition = base::GetBoolProperty("debug.sf.enable_gl_backpressure"s, true);
+    ALOGI_IF(mBackpressureGpuComposition, "Enabling backpressure for GPU composition");
 
     property_get("ro.surface_flinger.supports_background_blur", value, "0");
     bool supportsBlurs = atoi(value);
     mSupportsBlur = supportsBlurs;
     ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
-    property_get("ro.sf.blurs_are_expensive", value, "0");
-    mBlursAreExpensive = atoi(value);
 
-    const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
+    const size_t defaultListSize = MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
     mGraphicBufferProducerListSizeLogThreshold =
@@ -423,6 +450,9 @@
     property_get("debug.sf.treat_170m_as_sRGB", value, "0");
     mTreat170mAsSrgb = atoi(value);
 
+    mIgnoreHwcPhysicalDisplayOrientation =
+            base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
+
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
     // comes online to attempt to read the property. The property is
@@ -438,7 +468,12 @@
         android::hardware::details::setTrebleTestingOverride(true);
     }
 
-    mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+    // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner
+    mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+    mRefreshRateOverlayRenderRate =
+            property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
+    mRefreshRateOverlayShowInMiddle =
+            property_get_bool("debug.sf.show_refresh_rate_overlay_in_middle", 0);
 
     if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
         mTransactionTracing.emplace();
@@ -446,21 +481,21 @@
 
     mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
 
-    // Power hint session mode, representing which hint(s) to send: early, late, or both)
-    mPowerHintSessionMode =
-            {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
-             .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)};
+    mLayerLifecycleManagerEnabled =
+            base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false);
+    mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
+            base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
 }
 
 LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
-    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
-        return LatchUnsignaledConfig::Always;
-    }
-
     if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, true)) {
         return LatchUnsignaledConfig::AutoSingleLayer;
     }
 
+    if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
+        return LatchUnsignaledConfig::Always;
+    }
+
     return LatchUnsignaledConfig::Disabled;
 }
 
@@ -470,13 +505,13 @@
     // the window manager died on us. prepare its eulogy.
     mBootFinished = false;
 
-    // Sever the link to inputflinger since it's gone as well.
-    static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; }));
+    static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
+        // Sever the link to inputflinger since it's gone as well.
+        mInputFlinger.clear();
 
-    // restore initial conditions (default device unblank, etc)
-    initializeDisplays();
+        initializeDisplays();
+    }));
 
-    // restart the boot-animation
     startBootAnim();
 }
 
@@ -484,12 +519,8 @@
     mScheduler->run();
 }
 
-sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
-    const sp<Client> client = new Client(this);
-    return client->initCheck() == NO_ERROR ? client : nullptr;
-}
-
-sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
+sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure,
+                                          float requestedRefreshRate) {
     // onTransact already checks for some permissions, but adding an additional check here.
     // This is to ensure that only system and graphics can request to create a secure
     // display. Secure displays can show secure content so we add an additional restriction on it.
@@ -504,7 +535,7 @@
         virtual ~DisplayToken() {
              // no more references, this display must be terminated
              Mutex::Autolock _l(flinger->mStateLock);
-             flinger->mCurrentState.displays.removeItem(this);
+             flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this));
              flinger->setTransactionFlags(eDisplayTransactionNeeded);
          }
      public:
@@ -513,15 +544,15 @@
         }
     };
 
-    sp<BBinder> token = new DisplayToken(this);
+    sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this));
 
     Mutex::Autolock _l(mStateLock);
     // Display ID is assigned when virtual display is allocated by HWC.
     DisplayDeviceState state;
     state.isSecure = secure;
     state.displayName = displayName;
+    state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
     mCurrentState.displays.add(token, state);
-    mInterceptor->saveDisplayCreation(state);
     return token;
 }
 
@@ -539,7 +570,6 @@
         ALOGE("%s: Invalid operation on physical display", __func__);
         return;
     }
-    mInterceptor->saveDisplayDeletion(state.sequenceId);
     mCurrentState.displays.removeItemsAt(index);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
@@ -591,12 +621,12 @@
 
 std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
     std::vector<PhysicalDisplayId> displayIds;
-    displayIds.reserve(mPhysicalDisplayTokens.size());
+    displayIds.reserve(mPhysicalDisplays.size());
 
     const auto defaultDisplayId = getDefaultDisplayDeviceLocked()->getPhysicalId();
     displayIds.push_back(defaultDisplayId);
 
-    for (const auto& [id, token] : mPhysicalDisplayTokens) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
         if (id != defaultDisplayId) {
             displayIds.push_back(id);
         }
@@ -605,10 +635,10 @@
     return displayIds;
 }
 
-status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
-    Mutex::Autolock lock(mStateLock);
-    *id = getPrimaryDisplayIdLocked();
-    return NO_ERROR;
+std::optional<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdLocked(
+        const sp<display::DisplayToken>& displayToken) const {
+    return ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+            .transform(&ftl::to_key<PhysicalDisplays>);
 }
 
 sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const {
@@ -629,7 +659,7 @@
 }
 
 renderengine::RenderEngine& SurfaceFlinger::getRenderEngine() const {
-    return mCompositionEngine->getRenderEngine();
+    return *mRenderEngine;
 }
 
 compositionengine::CompositionEngine& SurfaceFlinger::getCompositionEngine() const {
@@ -661,7 +691,7 @@
     const String16 name("window");
     mWindowManager = defaultServiceManager()->getService(name);
     if (mWindowManager != 0) {
-        mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
+        mWindowManager->linkToDeath(sp<IBinder::DeathRecipient>::fromExisting(this));
     }
 
     // stop boot animation
@@ -675,7 +705,7 @@
 
     sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
 
-    static_cast<void>(mScheduler->schedule([=] {
+    static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) {
         if (input == nullptr) {
             ALOGE("Failed to link to input service");
         } else {
@@ -684,12 +714,12 @@
 
         readPersistentProperties();
         mPowerAdvisor->onBootFinished();
-        const bool powerHintEnabled = mFlagManager.use_adpf_cpu_hint();
-        mPowerAdvisor->enablePowerHint(powerHintEnabled);
-        const bool powerHintUsed = mPowerAdvisor->usePowerHintSession();
+        const bool hintSessionEnabled = mFlagManager.use_adpf_cpu_hint();
+        mPowerAdvisor->enablePowerHintSession(hintSessionEnabled);
+        const bool hintSessionUsed = mPowerAdvisor->usePowerHintSession();
         ALOGD("Power hint is %s",
-              powerHintUsed ? "supported" : (powerHintEnabled ? "unsupported" : "disabled"));
-        if (powerHintUsed) {
+              hintSessionUsed ? "supported" : (hintSessionEnabled ? "unsupported" : "disabled"));
+        if (hintSessionUsed) {
             std::optional<pid_t> renderEngineTid = getRenderEngine().getRenderEngineTid();
             std::vector<int32_t> tidList;
             tidList.emplace_back(gettid());
@@ -703,8 +733,9 @@
 
         mBootStage = BootStage::FINISHED;
 
-        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            FTL_FAKE_GUARD(mStateLock, enableRefreshRateOverlay(true));
+        if (base::GetBoolProperty("sf.debug.show_refresh_rate_overlay"s, false)) {
+            ftl::FakeGuard guard(mStateLock);
+            enableRefreshRateOverlay(true);
         }
     }));
 }
@@ -758,6 +789,10 @@
         return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
     } else if (strcmp(prop, "skiaglthreaded") == 0) {
         return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+    } else if (strcmp(prop, "skiavk") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::SKIA_VK;
+    } else if (strcmp(prop, "skiavkthreaded") == 0) {
+        return renderengine::RenderEngine::RenderEngineType::SKIA_VK_THREADED;
     } else {
         ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
         return {};
@@ -766,10 +801,11 @@
 
 // Do not call property_set on main thread which will be blocked by init
 // Use StartPropertySetThread instead.
-void SurfaceFlinger::init() {
+void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-    Mutex::Autolock _l(mStateLock);
+    addTransactionReadyFilters();
+    Mutex::Autolock lock(mStateLock);
 
     // Get a RenderEngine for the given display / config (can't fail)
     // TODO(b/77156734): We need to stop casting and use HAL types when possible.
@@ -788,7 +824,8 @@
     if (auto type = chooseRenderEngineTypeViaSysProp()) {
         builder.setRenderEngineType(type.value());
     }
-    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
+    mRenderEngine = renderengine::RenderEngine::create(builder.build());
+    mCompositionEngine->setRenderEngine(mRenderEngine.get());
     mMaxRenderTargetSize =
             std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
 
@@ -808,19 +845,44 @@
         enableHalVirtualDisplays(true);
     }
 
-    // Process any initial hotplug and resulting display changes.
-    processDisplayHotplugEventsLocked();
-    const auto display = getDefaultDisplayDeviceLocked();
-    LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
-    const auto displayId = display->getPhysicalId();
-    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
-                        "Primary display is disconnected.");
+    // Process hotplug for displays connected at boot.
+    LOG_ALWAYS_FATAL_IF(!configureLocked(),
+                        "Initial display configuration failed: HWC did not hotplug");
+
+    // Commit primary display.
+    sp<const DisplayDevice> display;
+    if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+        const auto& displays = mCurrentState.displays;
+
+        const auto& token = displays.keyAt(*indexOpt);
+        const auto& state = displays.valueAt(*indexOpt);
+
+        processDisplayAdded(token, state);
+        mDrawingState.displays.add(token, state);
+
+        display = getDefaultDisplayDeviceLocked();
+    }
+
+    LOG_ALWAYS_FATAL_IF(!display, "Failed to configure the primary display");
+    LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getPhysicalId()),
+                        "Primary display is disconnected");
+
+    // TODO(b/241285876): The Scheduler needlessly depends on creating the CompositionEngine part of
+    // the DisplayDevice, hence the above commit of the primary display. Remove that special case by
+    // initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
+    initScheduler(display);
+    dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+
+    // Commit secondary display(s).
+    processDisplayChangesLocked();
 
     // initialize our drawing state
     mDrawingState = mCurrentState;
 
-    // set initial conditions (e.g. unblank default device)
-    initializeDisplays();
+    onActiveDisplayChangedLocked(nullptr, *display);
+
+    static_cast<void>(mScheduler->schedule(
+            [this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); }));
 
     mPowerAdvisor->init();
 
@@ -838,8 +900,6 @@
         }
     }
 
-    onActiveDisplaySizeChanged(display);
-
     // Inform native graphics APIs whether the present timestamp is supported:
 
     const bool presentFenceReliable =
@@ -866,8 +926,8 @@
     property_get("persist.sys.sf.native_mode", value, "0");
     mDisplayColorSetting = static_cast<DisplayColorSetting>(atoi(value));
 
-    property_get("persist.sys.sf.color_mode", value, "0");
-    mForceColorMode = static_cast<ColorMode>(atoi(value));
+    mForceColorMode =
+            static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -882,17 +942,6 @@
 
 // ----------------------------------------------------------------------------
 
-bool SurfaceFlinger::authenticateSurfaceTexture(
-        const sp<IGraphicBufferProducer>& bufferProducer) const {
-    Mutex::Autolock _l(mStateLock);
-    return authenticateSurfaceTextureLocked(bufferProducer);
-}
-
-bool SurfaceFlinger::authenticateSurfaceTextureLocked(
-        const sp<IGraphicBufferProducer>& /* bufferProducer */) const {
-    return false;
-}
-
 status_t SurfaceFlinger::getSupportedFrameTimestamps(
         std::vector<FrameEvent>* outSupported) const {
     *outSupported = {
@@ -936,24 +985,24 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
-                                              ui::StaticDisplayInfo* info) {
-    if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+    if (!info) {
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mStateLock);
+    const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+    const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
 
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
+    if (!displayOpt) {
         return NAME_NOT_FOUND;
     }
 
-    if (const auto connectionType = display->getConnectionType())
-        info->connectionType = *connectionType;
-    else {
-        return INVALID_OPERATION;
-    }
+    const auto& [display, snapshotRef] = *displayOpt;
+    const auto& snapshot = snapshotRef.get();
+
+    info->connectionType = snapshot.connectionType();
+    info->deviceProductInfo = snapshot.deviceProductInfo();
 
     if (mEmulatedDisplayDensity) {
         info->density = mEmulatedDisplayDensity;
@@ -965,37 +1014,19 @@
     info->density /= ACONFIGURATION_DENSITY_MEDIUM;
 
     info->secure = display->isSecure();
-    info->deviceProductInfo = display->getDeviceProductInfo();
     info->installOrientation = display->getPhysicalOrientation();
 
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
-                                               ui::DynamicDisplayInfo* info) {
-    if (!displayToken || !info) {
-        return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mStateLock);
-
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
-        return NAME_NOT_FOUND;
-    }
-
-    const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-    if (!displayId) {
-        return INVALID_OPERATION;
-    }
-
-    info->activeDisplayModeId = display->getActiveMode()->getId().value();
-
-    const auto& supportedModes = display->getSupportedModes();
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+                                                   const sp<DisplayDevice>& display,
+                                                   const display::DisplaySnapshot& snapshot) {
+    const auto& displayModes = snapshot.displayModes();
     info->supportedDisplayModes.clear();
-    info->supportedDisplayModes.reserve(supportedModes.size());
+    info->supportedDisplayModes.reserve(displayModes.size());
 
-    for (const auto& [id, mode] : supportedModes) {
+    for (const auto& [id, mode] : displayModes) {
         ui::DisplayMode outMode;
         outMode.id = static_cast<int32_t>(id.value());
 
@@ -1035,106 +1066,206 @@
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
         outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+        excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+                                        outMode);
         info->supportedDisplayModes.push_back(outMode);
     }
 
+    info->supportedColorModes = snapshot.filterColorModes(mSupportsWideColor);
+
+    const PhysicalDisplayId displayId = snapshot.displayId();
+
+    const auto mode = display->refreshRateSelector().getActiveMode();
+    info->activeDisplayModeId = mode.modePtr->getId().value();
+    info->renderFrameRate = mode.fps.getValue();
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    info->supportedColorModes = getDisplayColorModes(*display);
-    info->hdrCapabilities = display->getHdrCapabilities();
+    info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
 
     info->autoLowLatencyModeSupported =
-            getHwComposer().hasDisplayCapability(*displayId,
+            getHwComposer().hasDisplayCapability(displayId,
                                                  DisplayCapability::AUTO_LOW_LATENCY_MODE);
     info->gameContentTypeSupported =
-            getHwComposer().supportsContentType(*displayId, hal::ContentType::GAME);
+            getHwComposer().supportsContentType(displayId, hal::ContentType::GAME);
 
     info->preferredBootDisplayMode = static_cast<ui::DisplayModeId>(-1);
 
     if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) {
-        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(*displayId)) {
-            if (const auto modeId = display->translateModeId(*hwcId)) {
+        if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) {
+            if (const auto modeId = snapshot.translateModeId(*hwcId)) {
                 info->preferredBootDisplayMode = modeId->value();
             }
         }
     }
-
-    return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) {
-    if (!stats) {
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+                                                     ui::DynamicDisplayInfo* info) {
+    if (!info) {
         return BAD_VALUE;
     }
 
-    *stats = mScheduler->getDisplayStatInfo(systemTime());
+    Mutex::Autolock lock(mStateLock);
+
+    const auto id_ =
+            DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+    const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+    if (!displayOpt) {
+        return NAME_NOT_FOUND;
+    }
+
+    const auto& [display, snapshotRef] = *displayOpt;
+    getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
     return NO_ERROR;
 }
 
-void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info, bool force) {
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                                        ui::DynamicDisplayInfo* info) {
+    if (!displayToken || !info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                    .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                    .and_then(getDisplayDeviceAndSnapshot());
+
+    if (!displayOpt) {
+        return NAME_NOT_FOUND;
+    }
+
+    const auto& [display, snapshotRef] = *displayOpt;
+    getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken,
+                                         DisplayStatInfo* outStats) {
+    if (!outStats) {
+        return BAD_VALUE;
+    }
+
+    std::optional<PhysicalDisplayId> displayIdOpt;
+    {
+        Mutex::Autolock lock(mStateLock);
+        if (displayToken) {
+            displayIdOpt = getPhysicalDisplayIdLocked(displayToken);
+            if (!displayIdOpt) {
+                ALOGW("%s: Invalid physical display token %p", __func__, displayToken.get());
+                return NAME_NOT_FOUND;
+            }
+        } else {
+            // TODO (b/277364366): Clients should be updated to pass in the display they
+            // want, rather than us picking an arbitrary one (the active display, in this
+            // case).
+            displayIdOpt = mActiveDisplayId;
+        }
+    }
+
+    const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt);
+    if (!schedule) {
+        ALOGE("%s: Missing VSYNC schedule for display %s!", __func__,
+              to_string(*displayIdOpt).c_str());
+        return NAME_NOT_FOUND;
+    }
+    outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns();
+    outStats->vsyncPeriod = schedule->period().ns();
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
     ATRACE_CALL();
 
-    if (!info.mode) {
-        ALOGW("requested display mode is null");
-        return;
-    }
-    auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
+    const auto displayId = request.mode.modePtr->getPhysicalDisplayId();
+    const auto display = getDisplayDeviceLocked(displayId);
     if (!display) {
         ALOGW("%s: display is no longer valid", __func__);
         return;
     }
 
-    if (display->setDesiredActiveMode(info, force)) {
-        scheduleComposite(FrameHint::kNone);
+    const auto mode = request.mode;
+    const bool emitEvent = request.emitEvent;
 
-        // Start receiving vsync samples now, so that we can detect a period
-        // switch.
-        mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
-        // As we called to set period, we will call to onRefreshRateChangeCompleted once
-        // VsyncController model is locked.
-        modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+    switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
+                                          force)) {
+        case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
+            // Set the render rate as setDesiredActiveMode updated it.
+            mScheduler->setRenderRate(displayId,
+                                      display->refreshRateSelector().getActiveMode().fps);
 
-        updatePhaseConfiguration(info.mode->getFps());
-        mScheduler->setModeChangePending(true);
+            // Schedule a new frame to initiate the display mode switch.
+            scheduleComposite(FrameHint::kNone);
+
+            // Start receiving vsync samples now, so that we can detect a period
+            // switch.
+            mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
+                                              mode.modePtr->getFps());
+
+            // As we called to set period, we will call to onRefreshRateChangeCompleted once
+            // VsyncController model is locked.
+            mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated);
+            updatePhaseConfiguration(mode.fps);
+            mScheduler->setModeChangePending(true);
+            break;
+        case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
+            mScheduler->setRenderRate(displayId, mode.fps);
+            updatePhaseConfiguration(mode.fps);
+            mRefreshRateStats->setRefreshRate(mode.fps);
+            if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+                mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+            }
+
+            break;
+        case DisplayDevice::DesiredActiveModeAction::None:
+            break;
     }
 }
 
-status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToken>& displayToken,
+                                                   DisplayModeId modeId) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    auto future = mScheduler->schedule([=]() -> status_t {
-        const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
-        if (!display) {
-            ALOGE("Attempt to set allowed display modes for invalid display token %p",
-                  displayToken.get());
+    const char* const whence = __func__;
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
+        const auto displayOpt =
+                FTL_FAKE_GUARD(mStateLock,
+                               ftl::find_if(mPhysicalDisplays,
+                                            PhysicalDisplay::hasToken(displayToken))
+                                       .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                                       .and_then(getDisplayDeviceAndSnapshot()));
+        if (!displayOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display modes for virtual display");
-            return INVALID_OPERATION;
-        }
+        const auto& [display, snapshotRef] = *displayOpt;
+        const auto& snapshot = snapshotRef.get();
 
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGW("Attempt to switch to an unsupported mode %d.", modeId);
+        const auto fpsOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getFps(); });
+
+        if (!fpsOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        const auto fps = mode->getFps();
-        // Keep the old switching type.
-        const auto allowGroupSwitching =
-                display->refreshRateConfigs().getCurrentPolicy().allowGroupSwitching;
-        const scheduler::RefreshRateConfigs::Policy policy{mode->getId(),
-                                                           allowGroupSwitching,
-                                                           {fps, fps}};
-        constexpr bool kOverridePolicy = false;
+        const Fps fps = *fpsOpt;
 
-        return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+        // Keep the old switching type.
+        const bool allowGroupSwitching =
+                display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
+
+        const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
+                                                                          {fps, fps},
+                                                                          allowGroupSwitching};
+
+        return setDesiredDisplayModeSpecsInternal(display, policy);
     });
 
     return future.get();
@@ -1148,52 +1279,61 @@
         return;
     }
 
-    const auto upcomingModeInfo =
-            FTL_FAKE_GUARD(kMainThreadContext, display->getUpcomingActiveMode());
-
-    if (!upcomingModeInfo.mode) {
+    const auto upcomingModeInfo = display->getUpcomingActiveMode();
+    if (!upcomingModeInfo.modeOpt) {
         // There is no pending mode change. This can happen if the active
         // display changed and the mode change happened on a different display.
         return;
     }
 
-    if (display->getActiveMode()->getResolution() != upcomingModeInfo.mode->getResolution()) {
+    if (display->getActiveMode().modePtr->getResolution() !=
+        upcomingModeInfo.modeOpt->modePtr->getResolution()) {
         auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
         // We need to generate new sequenceId in order to recreate the display (and this
         // way the framebuffer).
         state.sequenceId = DisplayDeviceState{}.sequenceId;
-        state.physical->activeMode = upcomingModeInfo.mode;
+        state.physical->activeMode = upcomingModeInfo.modeOpt->modePtr.get();
         processDisplayChangesLocked();
 
         // processDisplayChangesLocked will update all necessary components so we're done here.
         return;
     }
 
-    // We just created this display so we can call even if we are not on the main thread.
-    ftl::FakeGuard guard(kMainThreadContext);
-    display->setActiveMode(upcomingModeInfo.mode->getId());
+    mPhysicalDisplays.get(display->getPhysicalId())
+            .transform(&PhysicalDisplay::snapshotRef)
+            .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                FTL_FAKE_GUARD(kMainThreadContext,
+                               display->setActiveMode(upcomingModeInfo.modeOpt->modePtr->getId(),
+                                                      upcomingModeInfo.modeOpt->modePtr->getFps(),
+                                                      upcomingModeInfo.modeOpt->fps));
+            }));
 
-    const Fps refreshRate = upcomingModeInfo.mode->getFps();
+    const Fps refreshRate = upcomingModeInfo.modeOpt->fps;
     mRefreshRateStats->setRefreshRate(refreshRate);
     updatePhaseConfiguration(refreshRate);
 
-    if (upcomingModeInfo.event != DisplayModeEvent::None) {
-        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
+    if (upcomingModeInfo.event != scheduler::DisplayModeEvent::None) {
+        mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, *upcomingModeInfo.modeOpt);
     }
 }
 
 void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& display) {
     display->clearDesiredActiveModeState();
-    if (isDisplayActiveLocked(display)) {
+    if (display->getPhysicalId() == mActiveDisplayId) {
         mScheduler->setModeChangePending(false);
     }
 }
 
 void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) {
-    const auto refreshRate = display->getDesiredActiveMode()->mode->getFps();
+    const auto desiredActiveMode = display->getDesiredActiveMode();
+    const auto& modeOpt = desiredActiveMode->modeOpt;
+    const auto displayId = modeOpt->modePtr->getPhysicalDisplayId();
+    const auto displayFps = modeOpt->modePtr->getFps();
+    const auto renderFps = modeOpt->fps;
     clearDesiredActiveModeState(display);
-    mScheduler->resyncToHardwareVsync(true, refreshRate);
-    updatePhaseConfiguration(refreshRate);
+    mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps);
+    mScheduler->setRenderRate(displayId, renderFps);
+    updatePhaseConfiguration(renderFps);
 }
 
 void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
@@ -1201,39 +1341,44 @@
 
     std::optional<PhysicalDisplayId> displayToUpdateImmediately;
 
-    for (const auto& iter : mDisplays) {
-        const auto& display = iter.second;
-        if (!display || !display->isInternal()) {
+    for (const auto& [id, physical] : mPhysicalDisplays) {
+        const auto& snapshot = physical.snapshot();
+
+        if (snapshot.connectionType() != ui::DisplayConnectionType::Internal) {
             continue;
         }
 
+        const auto display = getDisplayDeviceLocked(id);
+        if (!display) continue;
+
         // Store the local variable to release the lock.
         const auto desiredActiveMode = display->getDesiredActiveMode();
         if (!desiredActiveMode) {
-            // No desired active mode pending to be applied
+            // No desired active mode pending to be applied.
             continue;
         }
 
-        if (!isDisplayActiveLocked(display)) {
-            // display is no longer the active display, so abort the mode change
+        if (id != mActiveDisplayId) {
+            // Display is no longer the active display, so abort the mode change.
             clearDesiredActiveModeState(display);
             continue;
         }
 
-        const auto desiredMode = display->getMode(desiredActiveMode->mode->getId());
-        if (!desiredMode) {
+        const auto desiredModeId = desiredActiveMode->modeOpt->modePtr->getId();
+        const auto displayModePtrOpt = snapshot.displayModes().get(desiredModeId);
+
+        if (!displayModePtrOpt) {
             ALOGW("Desired display mode is no longer supported. Mode ID = %d",
-                  desiredActiveMode->mode->getId().value());
+                  desiredModeId.value());
             clearDesiredActiveModeState(display);
             continue;
         }
 
-        const auto refreshRate = desiredMode->getFps();
-        ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
-              desiredMode->getId().value(), to_string(refreshRate).c_str(),
+        ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
+              to_string(displayModePtrOpt->get()->getFps()).c_str(),
               to_string(display->getId()).c_str());
 
-        if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+        if (display->getActiveMode() == desiredActiveMode->modeOpt) {
             // we are already in the requested mode, there is nothing left to do
             desiredActiveModeChangeDone(display);
             continue;
@@ -1243,7 +1388,7 @@
         // allowed modes might have changed by the time we process the refresh.
         // Make sure the desired mode is still allowed
         const auto displayModeAllowed =
-                display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
+                display->refreshRateSelector().isModeAllowed(*desiredActiveMode->modeOpt);
         if (!displayModeAllowed) {
             clearDesiredActiveModeState(display);
             continue;
@@ -1255,9 +1400,8 @@
         constraints.seamlessRequired = false;
         hal::VsyncPeriodChangeTimeline outTimeline;
 
-        const auto status = FTL_FAKE_GUARD(kMainThreadContext,
-                                           display->initiateModeChange(*desiredActiveMode,
-                                                                       constraints, &outTimeline));
+        const auto status =
+                display->initiateModeChange(*desiredActiveMode, constraints, &outTimeline);
 
         if (status != NO_ERROR) {
             // initiateModeChange may fail if a hotplug event is just about
@@ -1265,6 +1409,8 @@
             ALOGW("initiateModeChange failed: %d", status);
             continue;
         }
+
+        display->refreshRateSelector().onModeChangeInitiated();
         mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
 
         if (outTimeline.refreshRequired) {
@@ -1283,8 +1429,7 @@
 
         const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
         const auto desiredActiveMode = display->getDesiredActiveMode();
-        if (desiredActiveMode &&
-            display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+        if (desiredActiveMode && display->getActiveMode() == desiredActiveMode->modeOpt) {
             desiredActiveModeChangeDone(display);
         }
     }
@@ -1305,22 +1450,6 @@
     future.wait();
 }
 
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
-    auto modes = getHwComposer().getColorModes(display.getPhysicalId());
-
-    // If the display is internal and the configuration claims it's not wide color capable,
-    // filter out all wide color modes. The typical reason why this happens is that the
-    // hardware is not good enough to support GPU composition of wide color, and thus the
-    // OEMs choose to disable this capability.
-    if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
-        !hasWideColorDisplay) {
-        const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
-        modes.erase(newEnd, modes.end());
-    }
-
-    return modes;
-}
-
 status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                                    ui::DisplayPrimaries& primaries) {
     if (!displayToken) {
@@ -1329,13 +1458,13 @@
 
     Mutex::Autolock lock(mStateLock);
 
-    const auto display = getDisplayDeviceLocked(displayToken);
+    const auto display = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                                 .transform(&ftl::to_mapped_ref<PhysicalDisplays>);
     if (!display) {
         return NAME_NOT_FOUND;
     }
 
-    const auto connectionType = display->getConnectionType();
-    if (connectionType != ui::DisplayConnectionType::Internal) {
+    if (!display.transform(&PhysicalDisplay::isInternal).value()) {
         return INVALID_OPERATION;
     }
 
@@ -1344,31 +1473,32 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
+status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode mode) {
     if (!displayToken) {
         return BAD_VALUE;
     }
 
+    const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
-                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+        const auto displayOpt =
+                ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                        .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                        .and_then(getDisplayDeviceAndSnapshot());
+
+        if (!displayOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGW("Attempt to set active color mode %s (%d) for virtual display",
-                  decodeColorMode(mode).c_str(), mode);
-            return INVALID_OPERATION;
-        }
+        const auto& [display, snapshotRef] = *displayOpt;
+        const auto& snapshot = snapshotRef.get();
 
-        const auto modes = getDisplayColorModes(*display);
+        const auto modes = snapshot.filterColorModes(mSupportsWideColor);
         const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
 
-        if (mode < ColorMode::NATIVE || !exists) {
-            ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
-                  decodeColorMode(mode).c_str(), mode, displayToken.get());
+        if (mode < ui::ColorMode::NATIVE || !exists) {
+            ALOGE("%s: Invalid color mode %s (%d) for display %s", whence,
+                  decodeColorMode(mode).c_str(), mode, to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
@@ -1391,30 +1521,67 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setBootDisplayMode(const sp<IBinder>& displayToken,
-                                            ui::DisplayModeId modeId) {
+status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties) const {
+    const auto& aidlProperties = getHwComposer().getOverlaySupport();
+    // convert aidl OverlayProperties to gui::OverlayProperties
+    outProperties->combinations.reserve(aidlProperties.combinations.size());
+    for (const auto& combination : aidlProperties.combinations) {
+        std::vector<int32_t> pixelFormats;
+        pixelFormats.reserve(combination.pixelFormats.size());
+        std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
+                       std::back_inserter(pixelFormats),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        std::vector<int32_t> standards;
+        standards.reserve(combination.standards.size());
+        std::transform(combination.standards.cbegin(), combination.standards.cend(),
+                       std::back_inserter(standards),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        std::vector<int32_t> transfers;
+        transfers.reserve(combination.transfers.size());
+        std::transform(combination.transfers.cbegin(), combination.transfers.cend(),
+                       std::back_inserter(transfers),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        std::vector<int32_t> ranges;
+        ranges.reserve(combination.ranges.size());
+        std::transform(combination.ranges.cbegin(), combination.ranges.cend(),
+                       std::back_inserter(ranges),
+                       [](const auto& val) { return static_cast<int32_t>(val); });
+        gui::OverlayProperties::SupportedBufferCombinations outCombination;
+        outCombination.pixelFormats = std::move(pixelFormats);
+        outCombination.standards = std::move(standards);
+        outCombination.transfers = std::move(transfers);
+        outCombination.ranges = std::move(ranges);
+        outProperties->combinations.emplace_back(outCombination);
+    }
+    outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& displayToken,
+                                            DisplayModeId modeId) {
     const char* const whence = __func__;
     auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) -> status_t {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+        const auto snapshotOpt =
+                ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+                        .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+                        .transform(&PhysicalDisplay::snapshotRef);
+
+        if (!snapshotOpt) {
+            ALOGE("%s: Invalid physical display token %p", whence, displayToken.get());
             return NAME_NOT_FOUND;
         }
 
-        if (display->isVirtual()) {
-            ALOGE("%s: Invalid operation on virtual display", whence);
-            return INVALID_OPERATION;
-        }
+        const auto& snapshot = snapshotOpt->get();
+        const auto hwcIdOpt = snapshot.displayModes().get(modeId).transform(
+                [](const DisplayModePtr& mode) { return mode->getHwcId(); });
 
-        const auto displayId = display->getPhysicalId();
-        const auto mode = display->getMode(DisplayModeId{modeId});
-        if (!mode) {
-            ALOGE("%s: Invalid mode %d for display %s", whence, modeId,
-                  to_string(displayId).c_str());
+        if (!hwcIdOpt) {
+            ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+                  to_string(snapshot.displayId()).c_str());
             return BAD_VALUE;
         }
 
-        return getHwComposer().setBootDisplayMode(displayId, mode->getHwcId());
+        return getHwComposer().setBootDisplayMode(snapshot.displayId(), *hwcIdOpt);
     });
     return future.get();
 }
@@ -1432,6 +1599,92 @@
     return future.get();
 }
 
+status_t SurfaceFlinger::getHdrConversionCapabilities(
+        std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) const {
+    bool hdrOutputConversionSupport;
+    getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    if (hdrOutputConversionSupport == false) {
+        ALOGE("hdrOutputConversion is not supported by this device.");
+        return INVALID_OPERATION;
+    }
+    const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities();
+    for (auto capability : aidlConversionCapability) {
+        gui::HdrConversionCapability tempCapability;
+        tempCapability.sourceType = static_cast<int>(capability.sourceType);
+        tempCapability.outputType = static_cast<int>(capability.outputType);
+        tempCapability.addsLatency = capability.addsLatency;
+        hdrConversionCapabilities->push_back(tempCapability);
+    }
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setHdrConversionStrategy(
+        const gui::HdrConversionStrategy& hdrConversionStrategy,
+        int32_t* outPreferredHdrOutputType) {
+    bool hdrOutputConversionSupport;
+    getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    if (hdrOutputConversionSupport == false) {
+        ALOGE("hdrOutputConversion is not supported by this device.");
+        return INVALID_OPERATION;
+    }
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) mutable -> status_t {
+        using AidlHdrConversionStrategy =
+                aidl::android::hardware::graphics::common::HdrConversionStrategy;
+        using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+        AidlHdrConversionStrategy aidlConversionStrategy;
+        status_t status;
+        aidl::android::hardware::graphics::common::Hdr aidlPreferredHdrOutputType;
+        switch (hdrConversionStrategy.getTag()) {
+            case GuiHdrConversionStrategyTag::passthrough: {
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::passthrough>(
+                        hdrConversionStrategy.get<GuiHdrConversionStrategyTag::passthrough>());
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
+            }
+            case GuiHdrConversionStrategyTag::autoAllowedHdrTypes: {
+                auto autoHdrTypes =
+                        hdrConversionStrategy
+                                .get<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>();
+                std::vector<aidl::android::hardware::graphics::common::Hdr> aidlAutoHdrTypes;
+                for (auto type : autoHdrTypes) {
+                    aidlAutoHdrTypes.push_back(
+                            static_cast<aidl::android::hardware::graphics::common::Hdr>(type));
+                }
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::autoAllowedHdrTypes>(
+                        aidlAutoHdrTypes);
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
+            }
+            case GuiHdrConversionStrategyTag::forceHdrConversion: {
+                auto forceHdrConversion =
+                        hdrConversionStrategy
+                                .get<GuiHdrConversionStrategyTag::forceHdrConversion>();
+                aidlConversionStrategy.set<AidlHdrConversionStrategy::Tag::forceHdrConversion>(
+                        static_cast<aidl::android::hardware::graphics::common::Hdr>(
+                                forceHdrConversion));
+                status = getHwComposer().setHdrConversionStrategy(aidlConversionStrategy,
+                                                                  &aidlPreferredHdrOutputType);
+                *outPreferredHdrOutputType = static_cast<int32_t>(aidlPreferredHdrOutputType);
+                return status;
+            }
+        }
+    });
+    return future.get();
+}
+
+status_t SurfaceFlinger::getHdrOutputConversionSupport(bool* outSupport) const {
+    auto future = mScheduler->schedule([this] {
+        return getHwComposer().hasCapability(Capability::HDR_OUTPUT_CONVERSION_CONFIG);
+    });
+
+    *outSupport = future.get();
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
     const char* const whence = __func__;
     static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
@@ -1455,18 +1708,6 @@
     }));
 }
 
-status_t SurfaceFlinger::clearAnimationFrameStats() {
-    Mutex::Autolock _l(mStateLock);
-    mAnimFrameTracker.clearStats();
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const {
-    Mutex::Autolock _l(mStateLock);
-    mAnimFrameTracker.getStats(outStats);
-    return NO_ERROR;
-}
-
 status_t SurfaceFlinger::overrideHdrTypes(const sp<IBinder>& displayToken,
                                           const std::vector<ui::Hdr>& hdrTypes) {
     Mutex::Autolock lock(mStateLock);
@@ -1482,7 +1723,8 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+                                    bool* success) {
     *success = mTimeStats->onPullAtom(atomId, pulledData);
     return NO_ERROR;
 }
@@ -1557,34 +1799,11 @@
     }
 
     *outIsWideColorDisplay =
-            display->isPrimary() ? hasWideColorDisplay : display->hasWideColorGamut();
+            display->isPrimary() ? mSupportsWideColor : display->hasWideColorGamut();
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    auto future = mScheduler->schedule([=] {
-        Mutex::Autolock lock(mStateLock);
-
-        if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
-            mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
-        }
-    });
-
-    future.wait();
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::injectVSync(nsecs_t when) {
-    Mutex::Autolock lock(mStateLock);
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(when);
-    const auto expectedPresent = calculateExpectedPresentTime(stats);
-    return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent,
-                                   /*deadlineTimestamp=*/expectedPresent)
-            ? NO_ERROR
-            : BAD_VALUE;
-}
-
-status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
     outLayers->clear();
     auto future = mScheduler->schedule([=] {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
@@ -1615,8 +1834,12 @@
         return BAD_VALUE;
     }
 
-    const wp<Layer> stopLayer = fromHandle(stopLayerHandle);
-    mRegionSamplingThread->addListener(samplingArea, stopLayer, listener);
+    // LayerHandle::getLayer promotes the layer object in a binder thread but we will not destroy
+    // the layer here since the caller has a strong ref to the layer's handle.
+    const sp<Layer> stopLayer = LayerHandle::getLayer(stopLayerHandle);
+    mRegionSamplingThread->addListener(samplingArea,
+                                       stopLayer ? stopLayer->getSequence() : UNASSIGNED_LAYER_ID,
+                                       listener);
     return NO_ERROR;
 }
 
@@ -1681,17 +1904,6 @@
     return NO_ERROR;
 }
 
-bool SurfaceFlinger::hasVisibleHdrLayer(const sp<DisplayDevice>& display) {
-    bool hasHdrLayers = false;
-    mDrawingState.traverse([&,
-                            compositionDisplay = display->getCompositionDisplay()](Layer* layer) {
-        hasHdrLayers |= (layer->isVisible() &&
-                         compositionDisplay->includesLayer(layer->getCompositionEngineLayerFE()) &&
-                         isHdrDataspace(layer->getDataSpace()));
-    });
-    return hasHdrLayers;
-}
-
 status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
                                               const gui::DisplayBrightness& brightness) {
     if (!displayToken) {
@@ -1816,19 +2028,21 @@
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
-        ISurfaceComposer::VsyncSource vsyncSource,
-        ISurfaceComposer::EventRegistrationFlags eventRegistration) {
+        gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
+        const sp<IBinder>& layerHandle) {
     const auto& handle =
-            vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
+            vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
+            ? mSfConnectionHandle
+            : mAppConnectionHandle;
 
-    return mScheduler->createDisplayEventConnection(handle, eventRegistration);
+    return mScheduler->createDisplayEventConnection(handle, eventRegistration, layerHandle);
 }
 
 void SurfaceFlinger::scheduleCommit(FrameHint hint) {
     if (hint == FrameHint::kActive) {
         mScheduler->resetIdleTimer();
     }
-    mPowerAdvisor->notifyDisplayUpdateImminent();
+    mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
     mScheduler->scheduleFrame();
 }
 
@@ -1856,66 +2070,29 @@
 
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
-    const std::string tracePeriod = [vsyncPeriod]() {
-        if (ATRACE_ENABLED() && vsyncPeriod) {
-            std::stringstream ss;
-            ss << "(" << *vsyncPeriod << ")";
-            return ss.str();
-        }
-        return std::string();
-    }();
-    ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
+    ATRACE_NAME(vsyncPeriod
+                        ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str()
+                        : ftl::Concat(__func__, ' ', hwcDisplayId).c_str());
 
     Mutex::Autolock lock(mStateLock);
-    const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
-    if (displayId) {
-        const auto token = getPhysicalDisplayTokenLocked(*displayId);
-        const auto display = getDisplayDeviceLocked(token);
-        display->onVsync(timestamp);
+    if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) {
+        if (mScheduler->addResyncSample(*displayIdOpt, timestamp, vsyncPeriod)) {
+            // period flushed
+            mScheduler->modulateVsync(displayIdOpt, &VsyncModulator::onRefreshRateChangeCompleted);
+        }
     }
-
-    if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
-        return;
-    }
-
-    const bool isActiveDisplay =
-            displayId && getPhysicalDisplayTokenLocked(*displayId) == mActiveDisplayToken;
-    if (!isActiveDisplay) {
-        // For now, we don't do anything with non active display vsyncs.
-        return;
-    }
-
-    bool periodFlushed = false;
-    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
-    if (periodFlushed) {
-        modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
-    }
-}
-
-void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) {
-    std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-    *compositorTiming = getBE().mCompositorTiming;
 }
 
 void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
                                           hal::Connection connection) {
-    const bool connected = connection == hal::Connection::CONNECTED;
-    ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
-
-    // Only lock if we're not on the main thread. This function is normally
-    // called on a hwbinder thread, but for the primary display it's called on
-    // the main thread with the state lock already held, so don't attempt to
-    // acquire it here.
-    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
-    mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection});
-
-    if (std::this_thread::get_id() == mMainThreadId) {
-        // Process all pending hot plug events immediately if we are on the main thread.
-        processDisplayHotplugEventsLocked();
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection});
     }
 
-    setTransactionFlags(eDisplayTransactionNeeded);
+    if (mScheduler) {
+        mScheduler->scheduleConfigure();
+    }
 }
 
 void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged(
@@ -1943,40 +2120,62 @@
     mScheduler->forceNextResync();
 }
 
-void SurfaceFlinger::setVsyncEnabled(bool enabled) {
+void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
     ATRACE_CALL();
+    if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
+        const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos);
+        ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue());
+        static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+            {
+                {
+                    const auto display = getDisplayDeviceLocked(*displayId);
+                    FTL_FAKE_GUARD(kMainThreadContext,
+                                   display->updateRefreshRateOverlayRate(fps,
+                                                                         display->getActiveMode()
+                                                                                 .fps,
+                                                                         /* setByHwc */ true));
+                }
+            }
+        }));
+    }
+}
+
+void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) {
+    const char* const whence = __func__;
+    ATRACE_FORMAT("%s (%d) for %" PRIu64, whence, enabled, id.value);
 
     // On main thread to avoid race conditions with display power state.
     static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
-        mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
+        {
+            ftl::FakeGuard guard(kMainThreadContext);
+            if (auto schedule = mScheduler->getVsyncSchedule(id)) {
+                schedule->setPendingHardwareVsyncState(enabled);
+            }
+        }
 
-        if (const auto display = getDefaultDisplayDeviceLocked();
-            display && display->isPoweredOn()) {
-            setHWCVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
+        ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value);
+        if (const auto display = getDisplayDeviceLocked(id); display && display->isPoweredOn()) {
+            setHWCVsyncEnabled(id, enabled);
         }
     }));
 }
 
-SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() {
-    const auto now = systemTime();
-    const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod;
-    const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod;
-
-    size_t shift = 0;
-    if (!expectedPresentTimeIsTheNextVsync) {
-        shift = static_cast<size_t>((mExpectedPresentTime - now) / vsyncPeriod);
-        if (shift >= mPreviousPresentFences.size()) {
-            shift = mPreviousPresentFences.size() - 1;
-        }
-    }
-    ATRACE_FORMAT("previousFrameFence shift=%zu", shift);
-    return mPreviousPresentFences[shift];
+bool SurfaceFlinger::wouldPresentEarly(TimePoint frameTime, Period vsyncPeriod) const {
+    const bool isThreeVsyncsAhead = mExpectedPresentTime - frameTime > 2 * vsyncPeriod;
+    return isThreeVsyncsAhead ||
+            getPreviousPresentFence(frameTime, vsyncPeriod)->getSignalTime() !=
+            Fence::SIGNAL_TIME_PENDING;
 }
 
-bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
-    ATRACE_CALL();
-    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
+auto SurfaceFlinger::getPreviousPresentFence(TimePoint frameTime, Period vsyncPeriod) const
+        -> const FenceTimePtr& {
+    const bool isTwoVsyncsAhead = mExpectedPresentTime - frameTime > vsyncPeriod;
+    const size_t i = static_cast<size_t>(isTwoVsyncsAhead);
+    return mPreviousPresentFences[i].fenceTime;
+}
 
+bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) {
+    ATRACE_CALL();
     if (fence == FenceTime::NO_FENCE) {
         return false;
     }
@@ -1987,53 +2186,221 @@
     return status == -ETIME;
 }
 
-nsecs_t SurfaceFlinger::previousFramePresentTime() {
-    const std::shared_ptr<FenceTime>& fence = previousFrameFence().fenceTime;
+TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const {
+    const auto& schedule = mScheduler->getVsyncSchedule();
 
-    if (fence == FenceTime::NO_FENCE) {
-        return Fence::SIGNAL_TIME_INVALID;
+    const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime);
+    if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
+        return vsyncDeadline;
     }
 
-    return fence->getSignalTime();
+    // Inflate the expected present time if we're targeting the next vsync.
+    return vsyncDeadline + schedule->period();
 }
 
-nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) const {
-    // Inflate the expected present time if we're targetting the next vsync.
-    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
-                                                          : stats.vsyncTime + stats.vsyncPeriod;
+void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) {
+    Mutex::Autolock lock(mStateLock);
+    if (configureLocked()) {
+        setTransactionFlags(eDisplayTransactionNeeded);
+    }
 }
 
-bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
+bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
+                                                bool transactionsFlushed,
+                                                bool& outTransactionsAreEmpty) {
+    bool needsTraversal = false;
+    if (transactionsFlushed) {
+        needsTraversal |= commitMirrorDisplays(vsyncId);
+        needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
+        needsTraversal |= applyTransactions(update.transactions, vsyncId);
+    }
+    outTransactionsAreEmpty = !needsTraversal;
+    const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
+    if (shouldCommit) {
+        commitTransactions();
+    }
+
+    bool mustComposite = latchBuffers() || shouldCommit;
+    updateLayerGeometry();
+    return mustComposite;
+}
+
+void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
+    using Changes = frontend::RequestedLayerState::Changes;
+    if (snapshot.path.isClone() ||
+        !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
+        return;
+    }
+
+    const auto layerProps = scheduler::LayerProps{
+            .visible = snapshot.isVisible,
+            .bounds = snapshot.geomLayerBounds,
+            .transform = snapshot.geomLayerTransform,
+            .setFrameRateVote = snapshot.frameRate,
+            .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
+    };
+
+    auto it = mLegacyLayers.find(snapshot.sequence);
+    LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+                        snapshot.getDebugString().c_str());
+
+    if (snapshot.changes.test(Changes::Animation)) {
+        it->second->recordLayerHistoryAnimationTx(layerProps);
+    }
+
+    if (snapshot.changes.test(Changes::FrameRate)) {
+        it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
+    }
+
+    if (snapshot.changes.test(Changes::Buffer)) {
+        it->second->recordLayerHistoryBufferUpdate(layerProps);
+    }
+}
+
+bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update,
+                                          bool transactionsFlushed, bool& outTransactionsAreEmpty) {
+    using Changes = frontend::RequestedLayerState::Changes;
+    ATRACE_NAME("updateLayerSnapshots");
+    {
+        mLayerLifecycleManager.addLayers(std::move(update.newLayers));
+        mLayerLifecycleManager.applyTransactions(update.transactions);
+        mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles);
+        for (auto& legacyLayer : update.layerCreatedStates) {
+            sp<Layer> layer = legacyLayer.layer.promote();
+            if (layer) {
+                mLegacyLayers[layer->sequence] = layer;
+            }
+        }
+    }
+    if (mLayerLifecycleManager.getGlobalChanges().test(Changes::Hierarchy)) {
+        ATRACE_NAME("LayerHierarchyBuilder:update");
+        mLayerHierarchyBuilder.update(mLayerLifecycleManager.getLayers(),
+                                      mLayerLifecycleManager.getDestroyedLayers());
+    }
+
+    bool mustComposite = false;
+    mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions);
+
+    {
+        ATRACE_NAME("LayerSnapshotBuilder:update");
+        frontend::LayerSnapshotBuilder::Args
+                args{.root = mLayerHierarchyBuilder.getHierarchy(),
+                     .layerLifecycleManager = mLayerLifecycleManager,
+                     .displays = mFrontEndDisplayInfos,
+                     .displayChanges = mFrontEndDisplayInfosChanged,
+                     .globalShadowSettings = mDrawingState.globalShadowSettings,
+                     .supportsBlur = mSupportsBlur,
+                     .forceFullDamage = mForceFullDamage,
+                     .supportedLayerGenericMetadata =
+                             getHwComposer().getSupportedLayerGenericMetadata(),
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+        mLayerSnapshotBuilder.update(args);
+    }
+
+    if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input |
+                                                      Changes::Hierarchy | Changes::Visibility)) {
+        mUpdateInputInfo = true;
+    }
+    if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
+                                                      Changes::Visibility)) {
+        mVisibleRegionsDirty = true;
+    }
+    outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
+    mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
+
+    bool newDataLatched = false;
+    if (!mLegacyFrontEndEnabled) {
+        ATRACE_NAME("DisplayCallbackAndStatsUpdates");
+        applyTransactions(update.transactions, vsyncId);
+        const nsecs_t latchTime = systemTime();
+        bool unused = false;
+
+        for (auto& layer : mLayerLifecycleManager.getLayers()) {
+            if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
+                layer->bgColorLayer) {
+                sp<Layer> bgColorLayer = getFactory().createEffectLayer(
+                        LayerCreationArgs(this, nullptr, layer->name,
+                                          ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
+                                          std::make_optional(layer->id), true));
+                mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
+            }
+            const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
+            if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) continue;
+
+            auto it = mLegacyLayers.find(layer->id);
+            LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+                                layer->getDebugString().c_str());
+            const bool bgColorOnly =
+                    !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+            if (willReleaseBufferOnLatch) {
+                mLayersWithBuffersRemoved.emplace(it->second);
+            }
+            it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
+            mLayersWithQueuedFrames.emplace(it->second);
+        }
+
+        for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+            updateLayerHistory(*snapshot);
+            if (!snapshot->hasReadyFrame) continue;
+            newDataLatched = true;
+            if (!snapshot->isVisible) break;
+
+            Region visibleReg;
+            visibleReg.set(snapshot->transformedBoundsWithoutTransparentRegion);
+            invalidateLayerStack(snapshot->outputFilter, visibleReg);
+        }
+
+        for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
+            mLegacyLayers.erase(destroyedLayer->id);
+        }
+
+        {
+            ATRACE_NAME("LLM:commitChanges");
+            mLayerLifecycleManager.commitChanges();
+        }
+
+        commitTransactions();
+
+        // enter boot animation on first buffer latch
+        if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
+            ALOGI("Enter boot animation");
+            mBootStage = BootStage::BOOTANIMATION;
+        }
+    }
+    mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
+    return mustComposite;
+}
+
+bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    // calculate the expected present time once and use the cached
-    // value throughout this frame to make sure all layers are
-    // seeing this same value.
-    if (expectedVsyncTime >= frameTime) {
-        mExpectedPresentTime = expectedVsyncTime;
-    } else {
-        const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
-        mExpectedPresentTime = calculateExpectedPresentTime(stats);
-    }
-
-    const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
+    // The expectedVsyncTime, which was predicted when this frame was scheduled, is normally in the
+    // future relative to frameTime, but may not be for delayed frames. Adjust mExpectedPresentTime
+    // accordingly, but not mScheduledPresentTime.
+    const TimePoint lastScheduledPresentTime = mScheduledPresentTime;
     mScheduledPresentTime = expectedVsyncTime;
 
-    const auto vsyncIn = [&] {
-        if (!ATRACE_ENABLED()) return 0.f;
-        return (mExpectedPresentTime - systemTime()) / 1e6f;
-    }();
-    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
+    // Calculate the expected present time once and use the cached value throughout this frame to
+    // make sure all layers are seeing this same value.
+    mExpectedPresentTime = expectedVsyncTime >= frameTime ? expectedVsyncTime
+                                                          : calculateExpectedPresentTime(frameTime);
+
+    ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId.value,
+                  ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
                   mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
 
-    // When Backpressure propagation is enabled we want to give a small grace period
+    const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
+    const FenceTimePtr& previousPresentFence = getPreviousPresentFence(frameTime, vsyncPeriod);
+
+    // When backpressure propagation is enabled, we want to give a small grace period of 1ms
     // for the present fence to fire instead of just giving up on this frame to handle cases
     // where present fence is just about to get signaled.
-    const int graceTimeForPresentFenceMs =
-            (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0;
+    const int graceTimeForPresentFenceMs = static_cast<int>(
+            mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
 
     // Pending frames may trigger backpressure propagation.
     const TracedOrdinal<bool> framePending = {"PrevFramePending",
-                                              previousFramePending(graceTimeForPresentFenceMs)};
+                                              isFencePending(previousPresentFence,
+                                                             graceTimeForPresentFenceMs)};
 
     // Frame missed counts for metrics tracking.
     // A frame is missed if the prior frame is still pending. If no longer pending,
@@ -2043,18 +2410,22 @@
     // Add some slop to correct for drift. This should generally be
     // smaller than a typical frame duration, but should not be so small
     // that it reports reasonable drift as a missed frame.
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
-    const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
-    const nsecs_t previousPresentTime = previousFramePresentTime();
+    const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
+    const nsecs_t previousPresentTime = previousPresentFence->getSignalTime();
     const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
                                              framePending ||
                                                      (previousPresentTime >= 0 &&
-                                                      (lastScheduledPresentTime <
+                                                      (lastScheduledPresentTime.ns() <
                                                        previousPresentTime - frameMissedSlop))};
     const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
-                                                mHadDeviceComposition && frameMissed};
+                                                frameMissed &&
+                                                        mCompositionCoverage.test(
+                                                                CompositionCoverage::Hwc)};
+
     const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
-                                                mHadClientComposition && frameMissed};
+                                                frameMissed &&
+                                                        mCompositionCoverage.test(
+                                                                CompositionCoverage::Gpu)};
 
     if (frameMissed) {
         mFrameMissedCount++;
@@ -2069,6 +2440,11 @@
         mGpuFrameMissedCount++;
     }
 
+    if (mTracingEnabledChanged) {
+        mLayerTracingEnabled = mLayerTracing.isEnabled();
+        mTracingEnabledChanged = false;
+    }
+
     // If we are in the middle of a mode change and the fence hasn't
     // fired yet just wait for the next commit.
     if (mSetActiveModePending) {
@@ -2087,7 +2463,7 @@
     }
 
     if (framePending) {
-        if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
+        if (mBackpressureGpuComposition || (hwcFrameMissed && !gpuFrameMissed)) {
             scheduleCommit(FrameHint::kNone);
             return false;
         }
@@ -2095,33 +2471,24 @@
 
     // Save this once per commit + composite to ensure consistency
     // TODO (b/240619471): consider removing active display check once AOD is fixed
-    const auto activeDisplay =
-            FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken));
+    const auto activeDisplay = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayId));
     mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
             activeDisplay->getPowerMode() == hal::PowerMode::ON;
     if (mPowerHintSessionEnabled) {
-        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
-        // get stable vsync period from display mode
-        const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod();
         mPowerAdvisor->setCommitStart(frameTime);
         mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
-        const nsecs_t idealSfWorkDuration =
-                mVsyncModulator->getVsyncConfig().sfWorkDuration.count();
-        // Frame delay is how long we should have minus how long we actually have
-        mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime));
+
+        // Frame delay is how long we should have minus how long we actually have.
+        const Duration idealSfWorkDuration =
+                mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
+        const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
+
+        mPowerAdvisor->setFrameDelay(frameDelay);
         mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
-        mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
 
-        // Send early hint here to make sure there's not another frame pending
-        if (mPowerHintSessionMode.early) {
-            // Send a rough prediction for this frame based on last frame's timing info
-            mPowerAdvisor->sendPredictedWorkDuration();
-        }
-    }
-
-    if (mTracingEnabledChanged) {
-        mLayerTracingEnabled = mLayerTracing.isEnabled();
-        mTracingEnabledChanged = false;
+        const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+        const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod();
+        mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
     }
 
     if (mRefreshRateOverlaySpinner) {
@@ -2134,46 +2501,42 @@
     // Composite if transactions were committed, or if requested by HWC.
     bool mustComposite = mMustComposite.exchange(false);
     {
-        mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
+        mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(),
+                                    Fps::fromPeriodNsecs(vsyncPeriod.ns()));
 
-        bool needsTraversal = false;
-        if (clearTransactionFlags(eTransactionFlushNeeded)) {
-            // Locking:
-            // 1. to prevent onHandleDestroyed from being called while the state lock is held,
-            // we must keep a copy of the transactions (specifically the composer
-            // states) around outside the scope of the lock
-            // 2. Transactions and created layers do not share a lock. To prevent applying
-            // transactions with layers still in the createdLayer queue, flush the transactions
-            // before committing the created layers.
-            std::vector<TransactionState> transactions = flushTransactions();
-            needsTraversal |= commitCreatedLayers();
-            needsTraversal |= applyTransactions(transactions, vsyncId);
+        const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
+        frontend::Update updates;
+        if (flushTransactions) {
+            updates = flushLifecycleUpdates();
+            if (mTransactionTracing) {
+                mTransactionTracing->addCommittedTransactions(vsyncId.value, frameTime.ns(),
+                                                              updates, mFrontEndDisplayInfos,
+                                                              mFrontEndDisplayInfosChanged);
+            }
         }
-
-        const bool shouldCommit =
-                (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
-        if (shouldCommit) {
-            commitTransactions();
+        bool transactionsAreEmpty;
+        if (mLegacyFrontEndEnabled) {
+            mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
+                                                        transactionsAreEmpty);
+        }
+        if (mLayerLifecycleManagerEnabled) {
+            mustComposite |=
+                    updateLayerSnapshots(vsyncId, updates, flushTransactions, transactionsAreEmpty);
         }
 
         if (transactionFlushNeeded()) {
             setTransactionFlags(eTransactionFlushNeeded);
         }
 
-        mustComposite |= shouldCommit;
-        mustComposite |= latchBuffers();
-
         // This has to be called after latchBuffers because we want to include the layers that have
         // been latched in the commit callback
-        if (!needsTraversal) {
+        if (transactionsAreEmpty) {
             // Invoke empty transaction callbacks early.
             mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
         } else {
             // Invoke OnCommit callbacks.
             mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
         }
-
-        updateLayerGeometry();
     }
 
     // Layers need to get updated (in the previous line) before we can use them for
@@ -2181,41 +2544,72 @@
     // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
     // and may eventually call to ~Layer() if it holds the last reference
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
         mScheduler->chooseRefreshRateForContent();
         setActiveModeInHwcIfNeeded();
     }
 
     updateCursorAsync();
-    updateInputFlinger();
+    updateInputFlinger(vsyncId, frameTime);
 
     if (mLayerTracingEnabled && !mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and tracing should only be enabled for debugging.
-        mLayerTracing.notify(mVisibleRegionsDirty, frameTime);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
     }
+    mLastCommittedVsyncId = vsyncId;
 
     persistDisplayBrightness(mustComposite);
 
     return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
 }
 
-void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId)
+void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId)
         FTL_FAKE_GUARD(kMainThreadContext) {
-    ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
+    ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId.value);
 
     compositionengine::CompositionRefreshArgs refreshArgs;
     const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
     refreshArgs.outputs.reserve(displays.size());
     std::vector<DisplayId> displayIds;
     for (const auto& [_, display] : displays) {
-        refreshArgs.outputs.push_back(display->getCompositionDisplay());
+        bool dropFrame = false;
+        if (display->isVirtual()) {
+            Fps refreshRate = display->getAdjustedRefreshRate();
+            using fps_approx_ops::operator>;
+            dropFrame = (refreshRate > 0_Hz) && !mScheduler->isVsyncInPhase(frameTime, refreshRate);
+        }
+        if (!dropFrame) {
+            refreshArgs.outputs.push_back(display->getCompositionDisplay());
+        }
+        display->tracePowerMode();
         displayIds.push_back(display->getId());
     }
     mPowerAdvisor->setDisplays(displayIds);
-    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
-        if (auto layerFE = layer->getCompositionEngineLayerFE())
-            refreshArgs.layers.push_back(layerFE);
-    });
+
+    const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
+            compositionengine::Feature::kSnapshotLayerMetadata);
+    if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
+        updateLayerMetadataSnapshot();
+        mLayerMetadataSnapshotNeeded = false;
+    }
+
+    if (DOES_CONTAIN_BORDER) {
+        refreshArgs.borderInfoList.clear();
+        mDrawingState.traverse([&refreshArgs](Layer* layer) {
+            if (layer->isBorderEnabled()) {
+                compositionengine::BorderRenderInfo info;
+                info.width = layer->getBorderWidth();
+                info.color = layer->getBorderColor();
+                layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) {
+                    info.layerIds.push_back(ilayer->getSequence());
+                });
+                refreshArgs.borderInfoList.emplace_back(std::move(info));
+            }
+        });
+    }
+
+    refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
+
     refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
     for (auto layer : mLayersWithQueuedFrames) {
         if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -2230,8 +2624,7 @@
 
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
-    refreshArgs.blursAreExpensive = mBlursAreExpensive;
-    refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
+    refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags();
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -2245,69 +2638,108 @@
         refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
     }
 
-    const auto expectedPresentTime = mExpectedPresentTime.load();
-    const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
-    const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
-    refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
-    refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
+    const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period();
+
+    if (!getHwComposer().getComposer()->isSupported(
+                Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+        wouldPresentEarly(frameTime, vsyncPeriod)) {
+        const auto prevVsyncTime = mExpectedPresentTime - vsyncPeriod;
+        const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
+
+        refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
+    }
+
     refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
-    refreshArgs.expectedPresentTime = expectedPresentTime;
+    refreshArgs.expectedPresentTime = mExpectedPresentTime.ns();
+    refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
 
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
     const auto presentTime = systemTime();
 
+    std::vector<std::pair<Layer*, LayerFE*>> layers =
+            moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/false, vsyncId.value);
     mCompositionEngine->present(refreshArgs);
+    moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 
-    mTimeStats->recordFrameDuration(frameTime, systemTime());
-
-    // Send a power hint hint after presentation is finished
-    if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(),
-                                          systemTime());
-        if (mPowerHintSessionMode.late) {
-            mPowerAdvisor->sendActualWorkDuration();
+    for (auto [layer, layerFE] : layers) {
+        CompositionResult compositionResult{layerFE->stealCompositionResult()};
+        layer->onPreComposition(compositionResult.refreshStartTime);
+        for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
+            Layer* clonedFrom = layer->getClonedFrom().get();
+            auto owningLayer = clonedFrom ? clonedFrom : layer;
+            owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
         }
+        if (compositionResult.lastClientCompositionFence) {
+            layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+        }
+    }
+
+    mTimeStats->recordFrameDuration(frameTime.ns(), systemTime());
+
+    // Send a power hint after presentation is finished.
+    if (mPowerHintSessionEnabled) {
+        // Now that the current frame has been presented above, PowerAdvisor needs the present time
+        // of the previous frame (whose fence is signaled by now) to determine how long the HWC had
+        // waited on that fence to retire before presenting.
+        const auto& previousPresentFence = mPreviousPresentFences[0].fenceTime;
+
+        mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
+                                          TimePoint::now());
+        mPowerAdvisor->reportActualWorkDuration();
     }
 
     if (mScheduler->onPostComposition(presentTime)) {
         scheduleComposite(FrameHint::kNone);
     }
 
-    postFrame();
-    postComposition();
+    postComposition(presentTime);
 
-    const bool prevFrameHadClientComposition = mHadClientComposition;
+    const bool hadGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+    mCompositionCoverage.clear();
 
-    mHadClientComposition = mHadDeviceComposition = mReusedClientComposition = false;
     TimeStats::ClientCompositionRecord clientCompositionRecord;
     for (const auto& [_, display] : displays) {
         const auto& state = display->getCompositionDisplay()->getState();
-        mHadClientComposition |= state.usesClientComposition && !state.reusedClientComposition;
-        mHadDeviceComposition |= state.usesDeviceComposition;
-        mReusedClientComposition |= state.reusedClientComposition;
+
+        if (state.usesDeviceComposition) {
+            mCompositionCoverage |= CompositionCoverage::Hwc;
+        }
+
+        if (state.reusedClientComposition) {
+            mCompositionCoverage |= CompositionCoverage::GpuReuse;
+        } else if (state.usesClientComposition) {
+            mCompositionCoverage |= CompositionCoverage::Gpu;
+        }
+
         clientCompositionRecord.predicted |=
                 (state.strategyPrediction != CompositionStrategyPredictionState::DISABLED);
         clientCompositionRecord.predictionSucceeded |=
                 (state.strategyPrediction == CompositionStrategyPredictionState::SUCCESS);
     }
 
-    clientCompositionRecord.hadClientComposition = mHadClientComposition;
-    clientCompositionRecord.reused = mReusedClientComposition;
-    clientCompositionRecord.changed = prevFrameHadClientComposition != mHadClientComposition;
+    const bool hasGpuComposited = mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+    clientCompositionRecord.hadClientComposition = hasGpuComposited;
+    clientCompositionRecord.reused = mCompositionCoverage.test(CompositionCoverage::GpuReuse);
+    clientCompositionRecord.changed = hadGpuComposited != hasGpuComposited;
+
     mTimeStats->pushCompositionStrategyState(clientCompositionRecord);
 
-    // TODO: b/160583065 Enable skip validation when SF caches all client composition layers
-    const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
-    modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
+    using namespace ftl::flag_operators;
+
+    // TODO(b/160583065): Enable skip validation when SF caches all client composition layers.
+    const bool hasGpuUseOrReuse =
+            mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse);
+    mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse);
 
     mLayersWithQueuedFrames.clear();
     if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         // This will block and should only be used for debugging.
-        mLayerTracing.notify(mVisibleRegionsDirty, frameTime);
+        addToLayerTracing(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
     }
 
-    mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp
+    if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
     mVisibleRegionsDirty = false;
 
     if (mCompositionEngine->needsAnotherUpdate()) {
@@ -2315,7 +2747,7 @@
     }
 
     if (mPowerHintSessionEnabled) {
-        mPowerAdvisor->setCompositeEnd(systemTime());
+        mPowerAdvisor->setCompositeEnd(TimePoint::now());
     }
 }
 
@@ -2329,87 +2761,38 @@
     for (auto& layer : mLayersPendingRefresh) {
         Region visibleReg;
         visibleReg.set(layer->getScreenBounds());
-        invalidateLayerStack(layer, visibleReg);
+        invalidateLayerStack(layer->getOutputFilter(), visibleReg);
     }
     mLayersPendingRefresh.clear();
 }
 
-void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
-                                            std::shared_ptr<FenceTime>& presentFenceTime) {
-    // Update queue of past composite+present times and determine the
-    // most recently known composite to present latency.
-    getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime});
-    nsecs_t compositeToPresentLatency = -1;
-    while (!getBE().mCompositePresentTimes.empty()) {
-        SurfaceFlingerBE::CompositePresentTime& cpt = getBE().mCompositePresentTimes.front();
-        // Cached values should have been updated before calling this method,
-        // which helps avoid duplicate syscalls.
-        nsecs_t displayTime = cpt.display->getCachedSignalTime();
-        if (displayTime == Fence::SIGNAL_TIME_PENDING) {
-            break;
-        }
-        compositeToPresentLatency = displayTime - cpt.composite;
-        getBE().mCompositePresentTimes.pop();
-    }
-
-    // Don't let mCompositePresentTimes grow unbounded, just in case.
-    while (getBE().mCompositePresentTimes.size() > 16) {
-        getBE().mCompositePresentTimes.pop();
-    }
-
-    setCompositorTimingSnapped(stats, compositeToPresentLatency);
-}
-
-void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats,
-                                                nsecs_t compositeToPresentLatency) {
-    // Avoid division by 0 by defaulting to 60Hz
-    const auto vsyncPeriod = stats.vsyncPeriod ?: (60_Hz).getPeriodNsecs();
-
-    // Integer division and modulo round toward 0 not -inf, so we need to
-    // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0)
-            ? (vsyncPeriod -
-               (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % vsyncPeriod))
-            : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % vsyncPeriod);
-
-    // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval.
-    if (idealLatency <= 0) {
-        idealLatency = vsyncPeriod;
-    }
-
-    // Snap the latency to a value that removes scheduling jitter from the
-    // composition and present times, which often have >1ms of jitter.
-    // Reducing jitter is important if an app attempts to extrapolate
-    // something (such as user input) to an accurate diasplay time.
-    // Snapping also allows an app to precisely calculate
-    // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval).
-    const nsecs_t bias = vsyncPeriod / 2;
-    const int64_t extraVsyncs = ((compositeToPresentLatency - idealLatency + bias) / vsyncPeriod);
-    const nsecs_t snappedCompositeToPresentLatency =
-            (extraVsyncs > 0) ? idealLatency + (extraVsyncs * vsyncPeriod) : idealLatency;
-
-    std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-    getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency;
-    getBE().mCompositorTiming.interval = vsyncPeriod;
-    getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
-}
-
-bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
-    // Treat all layers as non-HDR if:
-    // 1. They do not have a valid HDR dataspace. Currently we treat those as PQ or HLG. and
-    // 2. The layer is allowed to be dimmed. WindowManager may disable dimming in order to
-    // keep animations invoking SDR screenshots of HDR layers seamless. Treat such tagged
-    // layers as HDR so that DisplayManagerService does not try to change the screen brightness
-    if (!isHdrDataspace(layer->getDataSpace()) && layer->isDimmingEnabled()) {
-        return false;
-    }
+bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
+    // Even though the camera layer may be using an HDR transfer function or otherwise be "HDR"
+    // the device may need to avoid boosting the brightness as a result of these layers to
+    // reduce power consumption during camera recording
     if (mIgnoreHdrCameraLayers) {
-        auto buffer = layer->getBuffer();
-        if (buffer && (buffer->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
+        if (snapshot.externalTexture &&
+            (snapshot.externalTexture->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
             return false;
         }
     }
-    return true;
+    if (isHdrDataspace(snapshot.dataspace)) {
+        return true;
+    }
+    // If the layer is not allowed to be dimmed, treat it as HDR. WindowManager may disable
+    // dimming in order to keep animations invoking SDR screenshots of HDR layers seamless.
+    // Treat such tagged layers as HDR so that DisplayManagerService does not try to change
+    // the screen brightness
+    if (!snapshot.dimmingEnabled) {
+        return true;
+    }
+    // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
+    if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
+                (int32_t)Dataspace::RANGE_EXTENDED &&
+        snapshot.desiredHdrSdrRatio > 1.01f) {
+        return true;
+    }
+    return false;
 }
 
 ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
@@ -2418,7 +2801,8 @@
     if (!id) {
         return ui::ROTATION_0;
     }
-    if (getHwComposer().getComposer()->isSupported(
+    if (!mIgnoreHwcPhysicalDisplayOrientation &&
+        getHwComposer().getComposer()->isSupported(
                 Hwc2::Composer::OptionalFeature::PhysicalDisplayOrientation)) {
         switch (getHwComposer().getPhysicalDisplayOrientation(*id)) {
             case Hwc2::AidlTransform::ROT_90:
@@ -2448,56 +2832,87 @@
     return ui::ROTATION_0;
 }
 
-void SurfaceFlinger::postComposition() {
+void SurfaceFlinger::postComposition(nsecs_t callTime) {
     ATRACE_CALL();
-    ALOGV("postComposition");
+    ALOGV(__func__);
 
-    const auto* display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+    const auto* defaultDisplay = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
 
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
+    if (defaultDisplay &&
+        defaultDisplay->getCompositionDisplay()->getState().usesClientComposition) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(display->getCompositionDisplay()
+                std::make_shared<FenceTime>(defaultDisplay->getCompositionDisplay()
                                                     ->getRenderSurface()
                                                     ->getClientTargetAcquireFence());
     } else {
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
-    for (size_t i = mPreviousPresentFences.size()-1; i >= 1; i--) {
-        mPreviousPresentFences[i] = mPreviousPresentFences[i-1];
-    }
+    mPreviousPresentFences[1] = mPreviousPresentFences[0];
 
-    mPreviousPresentFences[0].fence =
-            display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
-    mPreviousPresentFences[0].fenceTime =
-            std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
+    auto presentFence = defaultDisplay
+            ? getHwComposer().getPresentFence(defaultDisplay->getPhysicalId())
+            : Fence::NO_FENCE;
 
-    nsecs_t now = systemTime();
+    auto presentFenceTime = std::make_shared<FenceTime>(presentFence);
+    mPreviousPresentFences[0] = {presentFence, presentFenceTime};
+
+    const TimePoint presentTime = TimePoint::now();
 
     // 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(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime,
-                                 glCompositionDoneFenceTime);
-
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
+    mFrameTimeline->setSfPresent(presentTime.ns(), presentFenceTime, glCompositionDoneFenceTime);
 
     // 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 updateCompositorTiming has snapping logic.
-    updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
-                           mPreviousPresentFences[0].fenceTime);
-    CompositorTiming compositorTiming;
+    // but that should be okay since CompositorTiming has snapping logic.
+    const TimePoint compositeTime =
+            TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
+    const Duration presentLatency =
+            !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
+            ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime)
+            : Duration::zero();
+
+    const auto schedule = mScheduler->getVsyncSchedule();
+    const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
+    const Period vsyncPeriod = schedule->period();
+    const nsecs_t vsyncPhase = mVsyncConfiguration->getCurrentConfigs().late.sfOffset;
+
+    const CompositorTiming compositorTiming(vsyncDeadline.ns(), vsyncPeriod.ns(), vsyncPhase,
+                                            presentLatency.ns());
+
+    display::DisplayMap<ui::LayerStack, const DisplayDevice*> layerStackToDisplay;
     {
-        std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
-        compositorTiming = getBE().mCompositorTiming;
+        if (!mLayersWithBuffersRemoved.empty() || mNumTrustedPresentationListeners > 0) {
+            Mutex::Autolock lock(mStateLock);
+            for (const auto& [token, display] : mDisplays) {
+                layerStackToDisplay.emplace_or_replace(display->getLayerStack(), display.get());
+            }
+        }
     }
 
+    for (auto layer : mLayersWithBuffersRemoved) {
+        std::vector<ui::LayerStack> previouslyPresentedLayerStacks =
+                std::move(layer->mPreviouslyPresentedLayerStacks);
+        layer->mPreviouslyPresentedLayerStacks.clear();
+        for (auto layerStack : previouslyPresentedLayerStacks) {
+            auto optDisplay = layerStackToDisplay.get(layerStack);
+            if (optDisplay && !optDisplay->get()->isVirtual()) {
+                auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
+                layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+                                        ui::INVALID_LAYER_STACK);
+            }
+        }
+        layer->releasePendingBuffer(presentTime.ns());
+    }
+    mLayersWithBuffersRemoved.clear();
+
     for (const auto& layer: mLayersWithQueuedFrames) {
-        layer->onPostComposition(display, glCompositionDoneFenceTime,
-                                 mPreviousPresentFences[0].fenceTime, compositorTiming);
-        layer->releasePendingBuffer(/*dequeueReadyTime*/ now);
+        layer->onPostComposition(defaultDisplay, glCompositionDoneFenceTime, presentFenceTime,
+                                 compositorTiming);
+        layer->releasePendingBuffer(presentTime.ns());
     }
 
     std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
@@ -2524,17 +2939,23 @@
         mAddingHDRLayerInfoListener = false;
     }
 
-    if (haveNewListeners || mSomeDataspaceChanged || mVisibleRegionsWereDirtyThisFrame) {
+    if (haveNewListeners || mHdrLayerInfoChanged) {
         for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
             HdrLayerInfoReporter::HdrLayerInfo info;
             int32_t maxArea = 0;
             mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                 const auto layerFe = layer->getCompositionEngineLayerFE();
-                if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
-                    if (isHdrLayer(layer)) {
+                const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+                if (snapshot.isVisible &&
+                    compositionDisplay->includesLayer(snapshot.outputFilter)) {
+                    if (isHdrLayer(snapshot)) {
                         const auto* outputLayer =
                             compositionDisplay->getOutputLayerForLayer(layerFe);
                         if (outputLayer) {
+                            const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
+                                    ? std::numeric_limits<float>::infinity()
+                                    : snapshot.desiredHdrSdrRatio;
+                            info.mergeDesiredRatio(desiredHdrSdrRatio);
                             info.numberOfHdrLayers++;
                             const auto displayFrame = outputLayer->getState().displayFrame;
                             const int32_t area = displayFrame.width() * displayFrame.height();
@@ -2551,69 +2972,48 @@
         }
     }
 
-    mSomeDataspaceChanged = false;
-    mVisibleRegionsWereDirtyThisFrame = false;
+    mHdrLayerInfoChanged = false;
 
-    mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
+    mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
     mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
     mTransactionCallbackInvoker.clearCompletedTransactions();
 
-    if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
-        mPreviousPresentFences[0].fenceTime->isValid()) {
-        mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);
+    mTimeStats->incrementTotalFrames();
+    mTimeStats->setPresentFenceGlobal(presentFenceTime);
+
+    {
+        ftl::FakeGuard guard(mStateLock);
+        for (const auto& [id, physicalDisplay] : mPhysicalDisplays) {
+            if (auto displayDevice = getDisplayDeviceLocked(id);
+                displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) {
+                auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id
+                        ? std::move(presentFenceTime)
+                        : std::make_shared<FenceTime>(getHwComposer().getPresentFence(id));
+                if (presentFenceTimeI->isValid()) {
+                    mScheduler->addPresentFence(id, std::move(presentFenceTimeI));
+                }
+            }
+        }
     }
 
     const bool isDisplayConnected =
-            display && getHwComposer().isConnected(display->getPhysicalId());
+            defaultDisplay && getHwComposer().isConnected(defaultDisplay->getPhysicalId());
 
     if (!hasSyncFramework) {
-        if (isDisplayConnected && display->isPoweredOn()) {
-            mScheduler->enableHardwareVsync();
+        if (isDisplayConnected && defaultDisplay->isPoweredOn()) {
+            mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId());
         }
     }
 
-    if (mAnimCompositionPending) {
-        mAnimCompositionPending = false;
-
-        if (mPreviousPresentFences[0].fenceTime->isValid()) {
-            mAnimFrameTracker.setActualPresentFence(mPreviousPresentFences[0].fenceTime);
-        } else if (isDisplayConnected) {
-            // The HWC doesn't support present fences, so use the refresh
-            // timestamp instead.
-            const nsecs_t presentTime = display->getRefreshTimestamp();
-            mAnimFrameTracker.setActualPresentTime(presentTime);
-        }
-        mAnimFrameTracker.advanceFrame();
-    }
-
-    mTimeStats->incrementTotalFrames();
-
-    mTimeStats->setPresentFenceGlobal(mPreviousPresentFences[0].fenceTime);
-
     const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
     const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
     mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
 
-    if (isDisplayConnected && !display->isPoweredOn()) {
+    if (isDisplayConnected && !defaultDisplay->isPoweredOn()) {
         getRenderEngine().cleanupPostRender();
         return;
     }
 
-    nsecs_t currentTime = systemTime();
-    if (mHasPoweredOff) {
-        mHasPoweredOff = false;
-    } else {
-        nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime;
-        size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod);
-        if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) {
-            getBE().mFrameBuckets[numPeriods] += elapsedTime;
-        } else {
-            getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime;
-        }
-        getBE().mTotalTime += elapsedTime;
-    }
-    getBE().mLastSwapTime = currentTime;
-
     // Cleanup any outstanding resources due to rendering a prior frame.
     getRenderEngine().cleanupPostRender();
 
@@ -2634,12 +3034,33 @@
         }
     }
 
+    if (mNumTrustedPresentationListeners > 0) {
+        // We avoid any reverse traversal upwards so this shouldn't be too expensive
+        traverseLegacyLayers([&](Layer* layer) {
+            if (!layer->hasTrustedPresentationListener()) {
+                return;
+            }
+            const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled)
+                    ? mLayerSnapshotBuilder.getSnapshot(layer->sequence)
+                    : layer->getLayerSnapshot();
+            std::optional<const DisplayDevice*> displayOpt = std::nullopt;
+            if (snapshot) {
+                displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack);
+            }
+            const DisplayDevice* display = displayOpt.value_or(nullptr);
+            layer->updateTrustedPresentationState(display, snapshot,
+                                                  nanoseconds_to_milliseconds(callTime), false);
+        });
+    }
+
     // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
     // side-effect of getTotalSize(), so we check that again here
     if (ATRACE_ENABLED()) {
         // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
         ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
     }
+
+    logFrameStats(presentTime);
 }
 
 FloatRect SurfaceFlinger::getMaxDisplayBounds() {
@@ -2672,16 +3093,6 @@
     }
 }
 
-void SurfaceFlinger::postFrame() {
-    const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
-    if (display && getHwComposer().isConnected(display->getPhysicalId())) {
-        uint32_t flipCount = display->getPageFlipCount();
-        if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
-            logFrameStats();
-        }
-    }
-}
-
 void SurfaceFlinger::commitTransactions() {
     ATRACE_CALL();
 
@@ -2698,7 +3109,7 @@
     // so we can call commitTransactionsLocked unconditionally.
     // We clear the flags with mStateLock held to guarantee that
     // mCurrentState won't change until the transaction is committed.
-    modulateVsync(&VsyncModulator::onTransactionCommit);
+    mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit);
     commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
 
     mDebugInTransaction = 0;
@@ -2714,10 +3125,9 @@
     do {
         hwcModes = getHwComposer().getModes(displayId);
         activeModeHwcId = getHwComposer().getActiveMode(displayId);
-        LOG_ALWAYS_FATAL_IF(!activeModeHwcId, "HWC returned no active mode");
 
         const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
-            return mode.hwcId == *activeModeHwcId;
+            return mode.hwcId == activeModeHwcId;
         };
 
         if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) {
@@ -2725,16 +3135,21 @@
         }
     } while (++attempt < kMaxAttempts);
 
-    LOG_ALWAYS_FATAL_IF(attempt == kMaxAttempts,
-                        "After %d attempts HWC still returns an active mode which is not"
-                        " supported. Active mode ID = %" PRIu64 ". Supported modes = %s",
-                        kMaxAttempts, *activeModeHwcId, base::Join(hwcModes, ", ").c_str());
-
-    DisplayModes oldModes;
-    if (const auto token = getPhysicalDisplayTokenLocked(displayId)) {
-        oldModes = getDisplayDeviceLocked(token)->getSupportedModes();
+    if (attempt == kMaxAttempts) {
+        const std::string activeMode =
+                activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s;
+        ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, "
+              "hwcModes={%s}",
+              activeMode.c_str(), base::Join(hwcModes, ", ").c_str());
+        return {};
     }
 
+    const DisplayModes oldModes = mPhysicalDisplays.get(displayId)
+                                          .transform([](const PhysicalDisplay& display) {
+                                              return display.snapshot().displayModes();
+                                          })
+                                          .value_or(DisplayModes{});
+
     ui::DisplayModeId nextModeId = 1 +
             std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1),
                             [](ui::DisplayModeId max, const auto& pair) {
@@ -2772,70 +3187,96 @@
     return {modes, activeMode};
 }
 
-void SurfaceFlinger::processDisplayHotplugEventsLocked() {
-    for (const auto& event : mPendingHotplugEvents) {
-        std::optional<DisplayIdentificationInfo> info =
-                getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
-
-        if (!info) {
-            continue;
-        }
-
-        const auto displayId = info->id;
-        const auto token = mPhysicalDisplayTokens.get(displayId);
-
-        if (event.connection == hal::Connection::CONNECTED) {
-            auto [supportedModes, activeMode] = loadDisplayModes(displayId);
-
-            if (!token) {
-                ALOGV("Creating display %s", to_string(displayId).c_str());
-
-                DisplayDeviceState state;
-                state.physical = {.id = displayId,
-                                  .type = getHwComposer().getDisplayConnectionType(displayId),
-                                  .hwcDisplayId = event.hwcDisplayId,
-                                  .deviceProductInfo = std::move(info->deviceProductInfo),
-                                  .supportedModes = std::move(supportedModes),
-                                  .activeMode = std::move(activeMode)};
-                state.isSecure = true; // All physical displays are currently considered secure.
-                state.displayName = std::move(info->name);
-
-                sp<IBinder> token = new BBinder();
-                mCurrentState.displays.add(token, state);
-                mPhysicalDisplayTokens.try_emplace(displayId, std::move(token));
-                mInterceptor->saveDisplayCreation(state);
-            } else {
-                ALOGV("Recreating display %s", to_string(displayId).c_str());
-
-                auto& state = mCurrentState.displays.editValueFor(token->get());
-                state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
-                state.physical->supportedModes = std::move(supportedModes);
-                state.physical->activeMode = std::move(activeMode);
-                if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
-                    state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
-                }
-            }
-        } else {
-            ALOGV("Removing display %s", to_string(displayId).c_str());
-
-            if (const ssize_t index = mCurrentState.displays.indexOfKey(token->get()); index >= 0) {
-                const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
-                mInterceptor->saveDisplayDeletion(state.sequenceId);
-                mCurrentState.displays.removeItemsAt(index);
-            }
-
-            mPhysicalDisplayTokens.erase(displayId);
-        }
-
-        processDisplayChangesLocked();
+bool SurfaceFlinger::configureLocked() {
+    std::vector<HotplugEvent> events;
+    {
+        std::lock_guard<std::mutex> lock(mHotplugMutex);
+        events = std::move(mPendingHotplugEvents);
     }
 
-    mPendingHotplugEvents.clear();
+    for (const auto [hwcDisplayId, connection] : events) {
+        if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) {
+            const auto displayId = info->id;
+            const bool connected = connection == hal::Connection::CONNECTED;
+
+            if (const char* const log =
+                        processHotplug(displayId, hwcDisplayId, connected, std::move(*info))) {
+                ALOGI("%s display %s (HAL ID %" PRIu64 ")", log, to_string(displayId).c_str(),
+                      hwcDisplayId);
+            }
+        }
+    }
+
+    return !events.empty();
+}
+
+const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId,
+                                           hal::HWDisplayId hwcDisplayId, bool connected,
+                                           DisplayIdentificationInfo&& info) {
+    const auto displayOpt = mPhysicalDisplays.get(displayId);
+    if (!connected) {
+        LOG_ALWAYS_FATAL_IF(!displayOpt);
+        const auto& display = displayOpt->get();
+
+        if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
+            mCurrentState.displays.removeItemsAt(index);
+        }
+
+        mPhysicalDisplays.erase(displayId);
+        return "Disconnecting";
+    }
+
+    auto [displayModes, activeMode] = loadDisplayModes(displayId);
+    if (!activeMode) {
+        // TODO(b/241286153): Report hotplug failure to the framework.
+        ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
+        getHwComposer().disconnectDisplay(displayId);
+        return nullptr;
+    }
+
+    ui::ColorModes colorModes = getHwComposer().getColorModes(displayId);
+
+    if (displayOpt) {
+        const auto& display = displayOpt->get();
+        const auto& snapshot = display.snapshot();
+
+        std::optional<DeviceProductInfo> deviceProductInfo;
+        if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
+            deviceProductInfo = std::move(info.deviceProductInfo);
+        } else {
+            deviceProductInfo = snapshot.deviceProductInfo();
+        }
+
+        const auto it =
+                mPhysicalDisplays.try_replace(displayId, display.token(), displayId,
+                                              snapshot.connectionType(), std::move(displayModes),
+                                              std::move(colorModes), std::move(deviceProductInfo));
+
+        auto& state = mCurrentState.displays.editValueFor(it->second.token());
+        state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
+        state.physical->activeMode = std::move(activeMode);
+        return "Reconnecting";
+    }
+
+    const sp<IBinder> token = sp<BBinder>::make();
+
+    mPhysicalDisplays.try_emplace(displayId, token, displayId,
+                                  getHwComposer().getDisplayConnectionType(displayId),
+                                  std::move(displayModes), std::move(colorModes),
+                                  std::move(info.deviceProductInfo));
+
+    DisplayDeviceState state;
+    state.physical = {.id = displayId,
+                      .hwcDisplayId = hwcDisplayId,
+                      .activeMode = std::move(activeMode)};
+    state.isSecure = true; // All physical displays are currently considered secure.
+    state.displayName = std::move(info.name);
+
+    mCurrentState.displays.add(token, state);
+    return "Connecting";
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    ALOGI("Dispatching display hotplug event displayId=%s, connected=%d",
-          to_string(displayId).c_str(), connected);
     mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
     mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
 }
@@ -2846,7 +3287,8 @@
         const DisplayDeviceState& state,
         const sp<compositionengine::DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer) {
-    DisplayDeviceCreationArgs creationArgs(this, getHwComposer(), displayToken, compositionDisplay);
+    DisplayDeviceCreationArgs creationArgs(sp<SurfaceFlinger>::fromExisting(this), getHwComposer(),
+                                           displayToken, compositionDisplay);
     creationArgs.sequenceId = state.sequenceId;
     creationArgs.isSecure = state.isSecure;
     creationArgs.displaySurface = displaySurface;
@@ -2854,37 +3296,45 @@
     creationArgs.supportedPerFrameMetadata = 0;
 
     if (const auto& physical = state.physical) {
-        creationArgs.connectionType = physical->type;
-        creationArgs.supportedModes = physical->supportedModes;
         creationArgs.activeModeId = physical->activeMode->getId();
         const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
                 getKernelIdleTimerProperties(compositionDisplay->getId());
 
-        scheduler::RefreshRateConfigs::Config config =
-                {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+        using Config = scheduler::RefreshRateSelector::Config;
+        const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
+                ? Config::FrameRateOverride::Enabled
+                : Config::FrameRateOverride::Disabled;
+        Config config =
+                {.enableFrameRateOverride = enableFrameRateOverride,
                  .frameRateMultipleThreshold =
                          base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
                  .idleTimerTimeout = idleTimerTimeoutMs,
                  .kernelIdleTimerController = kernelIdleTimerController};
-        creationArgs.refreshRateConfigs =
-                std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
-                                                                creationArgs.activeModeId, config);
-    }
 
-    if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
-        creationArgs.isPrimary = id == getPrimaryDisplayIdLocked();
+        creationArgs.refreshRateSelector =
+                mPhysicalDisplays.get(physical->id)
+                        .transform(&PhysicalDisplay::snapshotRef)
+                        .transform([&](const display::DisplaySnapshot& snapshot) {
+                            return std::make_shared<
+                                    scheduler::RefreshRateSelector>(snapshot.displayModes(),
+                                                                    creationArgs.activeModeId,
+                                                                    config);
+                        })
+                        .value_or(nullptr);
+
+        creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
 
         if (useColorManagement) {
-            std::vector<ColorMode> modes = getHwComposer().getColorModes(*id);
-            for (ColorMode colorMode : modes) {
-                if (isWideColorMode(colorMode)) {
-                    creationArgs.hasWideColorGamut = true;
-                }
-
-                std::vector<RenderIntent> renderIntents =
-                        getHwComposer().getRenderIntents(*id, colorMode);
-                creationArgs.hwcColorModes.emplace(colorMode, renderIntents);
-            }
+            mPhysicalDisplays.get(physical->id)
+                    .transform(&PhysicalDisplay::snapshotRef)
+                    .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                        for (const auto mode : snapshot.colorModes()) {
+                            creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
+                            creationArgs.hwcColorModes
+                                    .emplace(mode,
+                                             getHwComposer().getRenderIntents(physical->id, mode));
+                        }
+                    }));
         }
     }
 
@@ -2912,27 +3362,35 @@
     creationArgs.initialPowerMode =
             state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt;
 
+    creationArgs.requestedRefreshRate = state.requestedRefreshRate;
+
     sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
 
     nativeWindowSurface->preallocateBuffers();
 
-    ColorMode defaultColorMode = ColorMode::NATIVE;
+    ui::ColorMode defaultColorMode = ui::ColorMode::NATIVE;
     Dataspace defaultDataSpace = Dataspace::UNKNOWN;
     if (display->hasWideColorGamut()) {
-        defaultColorMode = ColorMode::SRGB;
+        defaultColorMode = ui::ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
     display->getCompositionDisplay()->setColorProfile(
             compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
                                                     RenderIntent::COLORIMETRIC,
                                                     Dataspace::UNKNOWN});
-    if (!state.isVirtual()) {
-        FTL_FAKE_GUARD(kMainThreadContext,
-                       display->setActiveMode(state.physical->activeMode->getId()));
-        display->setDeviceProductInfo(state.physical->deviceProductInfo);
+
+    if (const auto& physical = state.physical) {
+        mPhysicalDisplays.get(physical->id)
+                .transform(&PhysicalDisplay::snapshotRef)
+                .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+                    FTL_FAKE_GUARD(kMainThreadContext,
+                                   display->setActiveMode(physical->activeMode->getId(),
+                                                          physical->activeMode->getFps(),
+                                                          physical->activeMode->getFps()));
+                }));
     }
 
-    display->setLayerStack(state.layerStack);
+    display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
     display->setProjection(state.orientation, state.layerStackSpaceRect,
                            state.orientedDisplaySpaceRect);
     display->setDisplayName(state.displayName);
@@ -3008,11 +3466,22 @@
     LOG_FATAL_IF(!displaySurface);
     auto display = setupNewDisplayDeviceInternal(displayToken, std::move(compositionDisplay), state,
                                                  displaySurface, producer);
-    if (display->isPrimary()) {
-        initScheduler(display);
+
+    if (mScheduler && !display->isVirtual()) {
+        const auto displayId = display->getPhysicalId();
+        {
+            // TODO(b/241285876): Annotate `processDisplayAdded` instead.
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            // For hotplug reconnect, renew the registration since display modes have been reloaded.
+            mScheduler->registerDisplay(displayId, display->holdRefreshRateSelector());
+        }
+
+        dispatchDisplayHotplugEvent(displayId, true);
     }
-    if (!state.isVirtual()) {
-        dispatchDisplayHotplugEvent(display->getPhysicalId(), true);
+
+    if (display->isVirtual()) {
+        display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
     }
 
     mDisplays.try_emplace(displayToken, std::move(display));
@@ -3027,6 +3496,7 @@
             releaseVirtualDisplay(display->getVirtualId());
         } else {
             dispatchDisplayHotplugEvent(display->getPhysicalId(), false);
+            mScheduler->unregisterDisplay(display->getPhysicalId());
         }
     }
 
@@ -3080,7 +3550,7 @@
 
             // TODO(b/175678251) Call a listener instead.
             if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
-                updateInternalDisplayVsyncLocked(display);
+                resetPhaseConfiguration(display->getActiveMode().fps);
             }
         }
         return;
@@ -3088,7 +3558,8 @@
 
     if (const auto display = getDisplayDeviceLocked(displayToken)) {
         if (currentState.layerStack != drawingState.layerStack) {
-            display->setLayerStack(currentState.layerStack);
+            display->setLayerFilter(
+                    makeLayerFilterForDisplay(display->getId(), currentState.layerStack));
         }
         if (currentState.flags != drawingState.flags) {
             display->setFlags(currentState.flags);
@@ -3098,23 +3569,28 @@
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
-            if (isDisplayActiveLocked(display)) {
+            if (display->getId() == mActiveDisplayId) {
                 mActiveDisplayTransformHint = display->getTransformHint();
+                sActiveDisplayRotationFlags =
+                        ui::Transform::toRotationFlags(display->getOrientation());
             }
         }
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
             display->setDisplaySize(currentState.width, currentState.height);
 
-            if (isDisplayActiveLocked(display)) {
-                onActiveDisplaySizeChanged(display);
+            if (display->getId() == mActiveDisplayId) {
+                onActiveDisplaySizeChanged(*display);
             }
         }
     }
 }
-void SurfaceFlinger::updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay) {
+
+void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) {
+    // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+    mScheduler->vsyncModulator().cancelRefreshRateChange();
+
     mVsyncConfiguration->reset();
-    const Fps refreshRate = activeDisplay->refreshRateConfigs().getActiveMode()->getFps();
     updatePhaseConfiguration(refreshRate);
     mRefreshRateStats->setRefreshRate(refreshRate);
 }
@@ -3127,6 +3603,7 @@
     const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
     if (!curr.isIdenticalTo(draw)) {
         mVisibleRegionsDirty = true;
+        mUpdateInputInfo = true;
 
         // find the displays that were removed
         // (ie: in drawing state but not in current state)
@@ -3162,15 +3639,20 @@
 void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) {
     // Commit display transactions.
     const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
-    if (displayTransactionNeeded) {
+    mFrontEndDisplayInfosChanged = displayTransactionNeeded;
+    if (displayTransactionNeeded && !mLayerLifecycleManagerEnabled) {
         processDisplayChangesLocked();
-        processDisplayHotplugEventsLocked();
+        mFrontEndDisplayInfos.clear();
+        for (const auto& [_, display] : mDisplays) {
+            mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+        }
     }
     mForceTransactionDisplayChange = displayTransactionNeeded;
 
     if (mSomeChildrenChanged) {
         mVisibleRegionsDirty = true;
         mSomeChildrenChanged = false;
+        mUpdateInputInfo = true;
     }
 
     // Update transform hint.
@@ -3215,14 +3697,19 @@
             if (!hintDisplay) {
                 // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
                 // redraw after transform hint changes. See bug 8508397.
-
                 // could be null when this layer is using a layerStack
                 // that is not visible on any display. Also can occur at
                 // screen off/on times.
-                hintDisplay = getDefaultDisplayDeviceLocked();
+                // U Update: Don't provide stale hints to the clients. For
+                // special cases where we want the app to draw its
+                // first frame before the display is available, we rely
+                // on WMS and DMS to provide the right information
+                // so the client can calculate the hint.
+                ALOGV("Skipping reporting transform hint update for %s", layer->getDebugName());
+                layer->skipReportingTransformHint();
+            } else {
+                layer->updateTransformHint(hintDisplay->getTransformHint());
             }
-
-            layer->updateTransformHint(hintDisplay->getTransformHint());
         });
     }
 
@@ -3230,6 +3717,7 @@
         mLayersAdded = false;
         // Layers have been added.
         mVisibleRegionsDirty = true;
+        mUpdateInputInfo = true;
     }
 
     // some layers might have been removed, so
@@ -3237,49 +3725,49 @@
     if (mLayersRemoved) {
         mLayersRemoved = false;
         mVisibleRegionsDirty = true;
+        mUpdateInputInfo = true;
         mDrawingState.traverseInZOrder([&](Layer* layer) {
-            if (mLayersPendingRemoval.indexOf(layer) >= 0) {
+            if (mLayersPendingRemoval.indexOf(sp<Layer>::fromExisting(layer)) >= 0) {
                 // this layer is not visible anymore
                 Region visibleReg;
                 visibleReg.set(layer->getScreenBounds());
-                invalidateLayerStack(layer, visibleReg);
+                invalidateLayerStack(layer->getOutputFilter(), visibleReg);
             }
         });
     }
 
+    if (transactionFlags & eInputInfoUpdateNeeded) {
+        mUpdateInputInfo = true;
+    }
+
     doCommitTransactions();
-    signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
-    mAnimTransactionPending = false;
 }
 
-void SurfaceFlinger::updateInputFlinger() {
-    ATRACE_CALL();
-    if (!mInputFlinger) {
+void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
+    if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
         return;
     }
+    ATRACE_CALL();
 
     std::vector<WindowInfo> windowInfos;
     std::vector<DisplayInfo> displayInfos;
     bool updateWindowInfo = false;
-    if (mVisibleRegionsDirty || mInputInfoChanged) {
-        mInputInfoChanged = false;
+    if (mUpdateInputInfo) {
+        mUpdateInputInfo = false;
         updateWindowInfo = true;
         buildWindowInfos(windowInfos, displayInfos);
     }
-    if (!updateWindowInfo && mInputWindowCommands.empty()) {
-        return;
-    }
 
-    std::unordered_set<Layer*> visibleLayers;
-    mDrawingState.traverse([&visibleLayers](Layer* layer) {
-        if (layer->isVisibleForInput()) {
-            visibleLayers.insert(layer);
+    std::unordered_set<int32_t> visibleWindowIds;
+    for (WindowInfo& windowInfo : windowInfos) {
+        if (!windowInfo.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
+            visibleWindowIds.insert(windowInfo.id);
         }
-    });
-    bool visibleLayersChanged = false;
-    if (visibleLayers != mVisibleLayers) {
-        visibleLayersChanged = true;
-        mVisibleLayers = std::move(visibleLayers);
+    }
+    bool visibleWindowsChanged = false;
+    if (visibleWindowIds != mVisibleWindowIds) {
+        visibleWindowsChanged = true;
+        mVisibleWindowIds = std::move(visibleWindowIds);
     }
 
     BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
@@ -3288,19 +3776,25 @@
                                                       inputWindowCommands =
                                                               std::move(mInputWindowCommands),
                                                       inputFlinger = mInputFlinger, this,
-                                                      visibleLayersChanged]() {
+                                                      visibleWindowsChanged, vsyncId, frameTime]() {
         ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
         if (updateWindowInfo) {
             mWindowInfosListenerInvoker
-                    ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos),
-                                         /* shouldSync= */ inputWindowCommands.syncInputWindows,
-                                         /* forceImmediateCall= */
-                                         visibleLayersChanged ||
+                    ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
+                                                                std::move(displayInfos),
+                                                                vsyncId.value, frameTime.ns()},
+                                         std::move(
+                                                 inputWindowCommands.windowInfosReportedListeners),
+                                         /* forceImmediateCall= */ visibleWindowsChanged ||
                                                  !inputWindowCommands.focusRequests.empty());
-        } else if (inputWindowCommands.syncInputWindows) {
-            // If the caller requested to sync input windows, but there are no
-            // changes to input windows, notify immediately.
-            windowInfosReported();
+        } else {
+            // If there are listeners but no changes to input windows, call the listeners
+            // immediately.
+            for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
+                if (IInterface::asBinder(listener)->isBinderAlive()) {
+                    listener->onWindowInfosReported();
+                }
+            }
         }
         for (const auto& focusRequest : inputWindowCommands.focusRequests) {
             inputFlinger->setFocusedWindow(focusRequest);
@@ -3332,7 +3826,7 @@
 
                 ALOGE_IF(error != NO_ERROR,
                          "Error setting display brightness for display %s: %d (%s)",
-                         display->getDebugName().c_str(), error, strerror(error));
+                         to_string(display->getId()).c_str(), error, strerror(error));
             }
             display->persistBrightness(needsComposite);
         }
@@ -3341,47 +3835,32 @@
 
 void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
                                       std::vector<DisplayInfo>& outDisplayInfos) {
-    ftl::SmallMap<ui::LayerStack, DisplayDevice::InputInfo, 4> displayInputInfos;
-
-    for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
-        const auto layerStack = display->getLayerStack();
-        const auto info = display->getInputInfo();
-
-        const auto [it, emplaced] = displayInputInfos.try_emplace(layerStack, info);
-        if (emplaced) {
-            continue;
-        }
-
-        // If the layer stack is mirrored on multiple displays, the first display that is configured
-        // to receive input takes precedence.
-        auto& otherInfo = it->second;
-        if (otherInfo.receivesInput) {
-            ALOGW_IF(display->receivesInput(),
-                     "Multiple displays claim to accept input for the same layer stack: %u",
-                     layerStack.id);
-        } else {
-            otherInfo = info;
-        }
-    }
-
     static size_t sNumWindowInfos = 0;
     outWindowInfos.reserve(sNumWindowInfos);
     sNumWindowInfos = 0;
 
-    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        if (!layer->needsInputInfo()) return;
+    if (mLayerLifecycleManagerEnabled) {
+        mLayerSnapshotBuilder.forEachInputSnapshot(
+                [&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
+                    outWindowInfos.push_back(snapshot.inputInfo);
+                });
+    } else {
+        mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+            if (!layer->needsInputInfo()) return;
+            const auto opt =
+                    mFrontEndDisplayInfos.get(layer->getLayerStack())
+                            .transform([](const frontend::DisplayInfo& info) {
+                                return Layer::InputDisplayArgs{&info.transform, info.isSecure};
+                            });
 
-        const auto opt = displayInputInfos.get(layer->getLayerStack(),
-                                               [](const auto& info) -> Layer::InputDisplayArgs {
-                                                   return {&info.transform, info.isSecure};
-                                               });
-        outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
-    });
+            outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
+        });
+    }
 
     sNumWindowInfos = outWindowInfos.size();
 
-    outDisplayInfos.reserve(displayInputInfos.size());
-    for (const auto& [_, info] : displayInputInfos) {
+    outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
+    for (const auto& [_, info] : mFrontEndDisplayInfos) {
         outDisplayInfos.push_back(info.info);
     }
 }
@@ -3393,35 +3872,49 @@
             refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
-
+    auto layers = moveSnapshotsToCompositionArgs(refreshArgs, /*cursorOnly=*/true, 0);
     mCompositionEngine->updateCursorAsync(refreshArgs);
+    moveSnapshotsFromCompositionArgs(refreshArgs, layers);
 }
 
-void SurfaceFlinger::requestDisplayMode(DisplayModePtr mode, DisplayModeEvent event) {
+void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest> modeRequests) {
+    if (mBootStage != BootStage::FINISHED) {
+        ALOGV("Currently in the boot stage, skipping display mode changes");
+        return;
+    }
+
+    ATRACE_CALL();
+
     // If this is called from the main thread mStateLock must be locked before
     // Currently the only way to call this function from the main thread is from
     // Scheduler::chooseRefreshRateForContent
 
     ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
 
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display || mBootStage != BootStage::FINISHED) {
-        return;
-    }
-    ATRACE_CALL();
+    for (auto& request : modeRequests) {
+        const auto& modePtr = request.mode.modePtr;
 
-    if (display->isInternal() && !isDisplayActiveLocked(display)) {
-        ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
-        return;
-    }
+        const auto displayId = modePtr->getPhysicalDisplayId();
+        const auto display = getDisplayDeviceLocked(displayId);
 
-    if (!display->refreshRateConfigs().isModeAllowed(mode->getId())) {
-        ALOGV("%s(%s): Disallowed mode %d", __func__, to_string(display->getId()).c_str(),
-              mode->getId().value());
-        return;
-    }
+        if (!display) continue;
 
-    setDesiredActiveMode({std::move(mode), event});
+        const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                               .transform(&PhysicalDisplay::isInternal)
+                                               .value_or(false);
+
+        if (isInternalDisplay && displayId != mActiveDisplayId) {
+            ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+            continue;
+        }
+
+        if (display->refreshRateSelector().isModeAllowed(request.mode)) {
+            setDesiredActiveMode(std::move(request));
+        } else {
+            ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
+                  to_string(display->getId()).c_str());
+        }
+    }
 }
 
 void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3433,24 +3926,19 @@
     mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
 }
 
-void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {
-    if (mScheduler) {
-        // If the scheduler is already initialized, this means that we received
-        // a hotplug(connected) on the primary display. In that case we should
-        // update the scheduler with the most recent display information.
-        ALOGW("Scheduler already initialized, updating instead");
-        mScheduler->setRefreshRateConfigs(display->holdRefreshRateConfigs());
-        return;
-    }
-    const auto currRefreshRate = display->getActiveMode()->getFps();
-    mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
-                                                                      hal::PowerMode::OFF);
+void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
+    using namespace scheduler;
 
-    mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
-    mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+    LOG_ALWAYS_FATAL_IF(mScheduler);
 
-    using Feature = scheduler::Feature;
-    scheduler::FeatureFlags features;
+    const auto activeMode = display->refreshRateSelector().getActiveMode();
+    const Fps activeRefreshRate = activeMode.fps;
+    mRefreshRateStats =
+            std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF);
+
+    mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
+
+    FeatureFlags features;
 
     if (sysprop::use_content_detection_for_refresh_rate(false)) {
         features |= Feature::kContentDetection;
@@ -3462,68 +3950,46 @@
         !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
         features |= Feature::kPresentFences;
     }
-
-    mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
-                                                        static_cast<ISchedulerCallback&>(*this),
-                                                        features);
-    {
-        auto configs = display->holdRefreshRateConfigs();
-        if (configs->kernelIdleTimerController().has_value()) {
-            features |= Feature::kKernelIdleTimer;
-        }
-
-        mScheduler->createVsyncSchedule(features);
-        mScheduler->setRefreshRateConfigs(std::move(configs));
+    if (display->refreshRateSelector().kernelIdleTimerController()) {
+        features |= Feature::kKernelIdleTimer;
     }
-    setVsyncEnabled(false);
+
+    auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+
+    mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
+                                             static_cast<ISchedulerCallback&>(*this), features,
+                                             std::move(modulatorPtr));
+    mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+
+    setVsyncEnabled(display->getPhysicalId(), false);
     mScheduler->startTimers();
 
     const auto configs = mVsyncConfiguration->getCurrentConfigs();
-    const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
-    mAppConnectionHandle =
-            mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
-                                         /*workDuration=*/configs.late.appWorkDuration,
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         impl::EventThread::InterceptVSyncsCallback());
-    mSfConnectionHandle =
-            mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
-                                         /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                                         /*readyDuration=*/configs.late.sfWorkDuration,
-                                         [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
 
-    mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
-                          configs.late.sfWorkDuration);
+    mAppConnectionHandle =
+            mScheduler->createEventThread(Scheduler::Cycle::Render,
+                                          mFrameTimeline->getTokenManager(),
+                                          /* workDuration */ configs.late.appWorkDuration,
+                                          /* readyDuration */ configs.late.sfWorkDuration);
+    mSfConnectionHandle =
+            mScheduler->createEventThread(Scheduler::Cycle::LastComposite,
+                                          mFrameTimeline->getTokenManager(),
+                                          /* workDuration */ activeRefreshRate.getPeriod(),
+                                          /* readyDuration */ configs.late.sfWorkDuration);
+
+    mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(),
+                          *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
 
     mRegionSamplingThread =
-            new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
-    mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
-    // Dispatch a mode change request for the primary display on scheduler
-    // initialization, so that the EventThreads always contain a reference to a
-    // prior configuration.
-    //
-    // This is a bit hacky, but this avoids a back-pointer into the main SF
-    // classes from EventThread, and there should be no run-time binder cost
-    // anyway since there are no connected apps at this point.
-    mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
+            sp<RegionSamplingThread>::make(*this,
+                                           RegionSamplingThread::EnvironmentTimingTunables());
+    mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
 }
 
-void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
+void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
     mVsyncConfiguration->setRefreshRateFps(refreshRate);
-    setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
-                   refreshRate.getPeriodNsecs());
-}
-
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
-                                    nsecs_t vsyncPeriod) {
-    mScheduler->setDuration(mAppConnectionHandle,
-                            /*workDuration=*/config.appWorkDuration,
-                            /*readyDuration=*/config.sfWorkDuration);
-    mScheduler->setDuration(mSfConnectionHandle,
-                            /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
-                            /*readyDuration=*/config.sfWorkDuration);
-    mScheduler->setDuration(config.sfWorkDuration);
+    mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(),
+                                  refreshRate.getPeriod());
 }
 
 void SurfaceFlinger::doCommitTransactions() {
@@ -3556,10 +4022,6 @@
         mLayersPendingRemoval.clear();
     }
 
-    // If this transaction is part of a window animation then the next frame
-    // we composite should be considered an animation as well.
-    mAnimCompositionPending = mAnimTransactionPending;
-
     mDrawingState = mCurrentState;
     // clear the "changed" flags in current state
     mCurrentState.colorMatrixChanged = false;
@@ -3571,8 +4033,24 @@
     }
 
     commitOffscreenLayers();
-    if (mNumClones > 0) {
-        mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
+    if (mLayerMirrorRoots.size() > 0) {
+        std::deque<Layer*> pendingUpdates;
+        pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
+                              mLayerMirrorRoots.end());
+        std::vector<Layer*> needsUpdating;
+        for (Layer* cloneRoot : mLayerMirrorRoots) {
+            pendingUpdates.pop_front();
+            if (cloneRoot->isRemovedFromCurrentState()) {
+                continue;
+            }
+            if (cloneRoot->updateMirrorInfo(pendingUpdates)) {
+            } else {
+                needsUpdating.push_back(cloneRoot);
+            }
+        }
+        for (Layer* cloneRoot : needsUpdating) {
+            cloneRoot->updateMirrorInfo({});
+        }
     }
 }
 
@@ -3587,10 +4065,10 @@
     }
 }
 
-void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
+void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) {
     for (const auto& [token, displayDevice] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
         auto display = displayDevice->getCompositionDisplay();
-        if (display->includesLayer(layer->getOutputFilter())) {
+        if (display->includesLayer(layerFilter)) {
             display->editState().dirtyRegion.orSelf(dirty);
         }
     }
@@ -3605,8 +4083,6 @@
     bool frameQueued = false;
     bool newDataLatched = false;
 
-    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-
     // Store the set of layers that need updates. This set must not change as
     // buffers are being latched, as this could result in a deadlock.
     // Example: Two producers share the same command stream and:
@@ -3624,16 +4100,19 @@
             }
         }
 
-        if (layer->hasReadyFrame()) {
+        if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) {
             frameQueued = true;
-            if (layer->shouldPresentNow(expectedPresentTime)) {
-                mLayersWithQueuedFrames.emplace(layer);
-            } else {
-                ATRACE_NAME("!layer->shouldPresentNow()");
-                layer->useEmptyDamage();
-            }
+            mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
         } else {
             layer->useEmptyDamage();
+            if (!layer->hasBuffer()) {
+                // The last latch time is used to classify a missed frame as buffer stuffing
+                // instead of a missed frame. This is used to identify scenarios where we
+                // could not latch a buffer or apply a transaction due to backpressure.
+                // We only update the latch time for buffer less layers here, the latch time
+                // is updated for buffer layers when the buffer is latched.
+                layer->updateLastLatchTime(latchTime);
+            }
         }
     });
     mForceTransactionDisplayChange = false;
@@ -3652,7 +4131,10 @@
         Mutex::Autolock lock(mStateLock);
 
         for (const auto& layer : mLayersWithQueuedFrames) {
-            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
+            if (layer->willReleaseBufferOnLatch()) {
+                mLayersWithBuffersRemoved.emplace(layer);
+            }
+            if (layer->latchBuffer(visibleRegions, latchTime)) {
                 mLayersPendingRefresh.push_back(layer);
                 newDataLatched = true;
             }
@@ -3675,7 +4157,7 @@
         mBootStage = BootStage::BOOTANIMATION;
     }
 
-    if (mNumClones > 0) {
+    if (mLayerMirrorRoots.size() > 0) {
         mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
     }
 
@@ -3683,44 +4165,80 @@
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
-status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
+status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
                                         const sp<Layer>& layer, const wp<Layer>& parent,
-                                        bool addToRoot, uint32_t* outTransformHint) {
-    if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
+                                        uint32_t* outTransformHint) {
+    if (mNumLayers >= MAX_LAYERS) {
         ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
-              ISurfaceComposer::MAX_LAYERS);
+              MAX_LAYERS);
         static_cast<void>(mScheduler->schedule([=] {
-            ALOGE("Dumping random sampling of on-screen layers: ");
-            mDrawingState.traverse([&](Layer *layer) {
+            ALOGE("Dumping layer keeping > 20 children alive:");
+            bool leakingParentLayerFound = false;
+            mDrawingState.traverse([&](Layer* layer) {
+                if (leakingParentLayerFound) {
+                    return;
+                }
+                if (layer->getChildrenCount() > 20) {
+                    leakingParentLayerFound = true;
+                    sp<Layer> parent = sp<Layer>::fromExisting(layer);
+                    while (parent) {
+                        ALOGE("Parent Layer: %s%s", parent->getName().c_str(),
+                              (parent->isHandleAlive() ? "handleAlive" : ""));
+                        parent = parent->getParent();
+                    }
+                    // Sample up to 100 layers
+                    ALOGE("Dumping random sampling of child layers total(%zu): ",
+                          layer->getChildrenCount());
+                    int sampleSize = (layer->getChildrenCount() / 100) + 1;
+                    layer->traverseChildren([&](Layer* layer) {
+                        if (rand() % sampleSize == 0) {
+                            ALOGE("Child Layer: %s", layer->getName().c_str());
+                        }
+                    });
+                }
+            });
+
+            int numLayers = 0;
+            mDrawingState.traverse([&](Layer* layer) { numLayers++; });
+
+            ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers);
+            mDrawingState.traverse([&](Layer* layer) {
                 // Aim to dump about 200 layers to avoid totally trashing
                 // logcat. On the other hand, if there really are 4096 layers
                 // something has gone totally wrong its probably the most
                 // useful information in logcat.
                 if (rand() % 20 == 13) {
-                    ALOGE("Layer: %s", layer->getName().c_str());
+                    ALOGE("Layer: %s%s", layer->getName().c_str(),
+                          (layer->isHandleAlive() ? "handleAlive" : ""));
+                    std::this_thread::sleep_for(std::chrono::milliseconds(5));
                 }
             });
+            ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
+                  mOffscreenLayers.size());
             for (Layer* offscreenLayer : mOffscreenLayers) {
                 if (rand() % 20 == 13) {
-                    ALOGE("Offscreen-layer: %s", offscreenLayer->getName().c_str());
+                    ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(),
+                          (offscreenLayer->isHandleAlive() ? "handleAlive" : ""));
+                    std::this_thread::sleep_for(std::chrono::milliseconds(5));
                 }
             }
         }));
         return NO_MEMORY;
     }
 
-    {
-        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
-        mCreatedLayers.emplace_back(layer, parent, addToRoot);
-    }
-
     layer->updateTransformHint(mActiveDisplayTransformHint);
     if (outTransformHint) {
         *outTransformHint = mActiveDisplayTransformHint;
     }
-    // attach this layer to the client
-    if (client != nullptr) {
-        client->attachLayer(handle, layer);
+    args.parentId = LayerHandle::getLayerId(args.parentHandle.promote());
+    args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
+    {
+        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+        mCreatedLayers.emplace_back(layer, parent, args.addToRoot);
+        mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
+        args.mirrorLayerHandle.clear();
+        args.parentHandle.clear();
+        mNewLayerArgs.emplace_back(std::move(args));
     }
 
     setTransactionFlags(eTransactionNeeded);
@@ -3732,217 +4250,171 @@
 }
 
 uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
-    return mTransactionFlags.fetch_and(~mask) & mask;
+    uint32_t transactionFlags = mTransactionFlags.fetch_and(~mask);
+    ATRACE_INT("mTransactionFlags", transactionFlags);
+    return transactionFlags & mask;
 }
 
 void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
                                          const sp<IBinder>& applyToken, FrameHint frameHint) {
-    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+    mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
+    uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
+    ATRACE_INT("mTransactionFlags", transactionFlags);
 
-    if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
+    if (const bool scheduled = transactionFlags & mask; !scheduled) {
         scheduleCommit(frameHint);
+    } else if (frameHint == FrameHint::kActive) {
+        // Even if the next frame is already scheduled, we should reset the idle timer
+        // as a new activity just happened.
+        mScheduler->resetIdleTimer();
     }
 }
 
-bool SurfaceFlinger::stopTransactionProcessing(
-        const std::unordered_set<sp<IBinder>, SpHash<IBinder>>&
-                applyTokensWithUnsignaledTransactions) const {
-    if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
-        // if we are in LatchUnsignaledConfig::AutoSingleLayer
-        // then we should have only one applyToken for processing.
-        // so we can stop further transactions on this applyToken.
-        return !applyTokensWithUnsignaledTransactions.empty();
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyTimelineCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    const auto& transaction = *flushState.transaction;
+    TimePoint desiredPresentTime = TimePoint::fromNs(transaction.desiredPresentTime);
+    // Do not present if the desiredPresentTime has not passed unless it is more than
+    // one second in the future. We ignore timestamps more than 1 second in the future
+    // for stability reasons.
+    if (!transaction.isAutoTimestamp && desiredPresentTime >= mExpectedPresentTime &&
+        desiredPresentTime < mExpectedPresentTime + 1s) {
+        ATRACE_FORMAT("not current desiredPresentTime: %" PRId64 " expectedPresentTime: %" PRId64,
+                      desiredPresentTime, mExpectedPresentTime);
+        return TransactionReadiness::NotReady;
     }
 
-    return false;
+    if (!mScheduler->isVsyncValid(mExpectedPresentTime, transaction.originUid)) {
+        ATRACE_FORMAT("!isVsyncValid expectedPresentTime: %" PRId64 " uid: %d",
+                      mExpectedPresentTime, transaction.originUid);
+        return TransactionReadiness::NotReady;
+    }
+
+    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the
+    // expected present time of this transaction.
+    if (transaction.isAutoTimestamp &&
+        frameIsEarly(mExpectedPresentTime, VsyncId{transaction.frameTimelineInfo.vsyncId})) {
+        ATRACE_FORMAT("frameIsEarly vsyncId: %" PRId64 " expectedPresentTime: %" PRId64,
+                      transaction.frameTimelineInfo.vsyncId, mExpectedPresentTime);
+        return TransactionReadiness::NotReady;
+    }
+    return TransactionReadiness::Ready;
 }
 
-int SurfaceFlinger::flushUnsignaledPendingTransactionQueues(
-        std::vector<TransactionState>& transactions,
-        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions) {
-    return flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                         applyTokensWithUnsignaledTransactions,
-                                         /*tryApplyUnsignaled*/ true);
-}
-
-int SurfaceFlinger::flushPendingTransactionQueues(
-        std::vector<TransactionState>& transactions,
-        std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
-        bool tryApplyUnsignaled) {
-    int transactionsPendingBarrier = 0;
-    auto it = mPendingTransactionQueues.begin();
-    while (it != mPendingTransactionQueues.end()) {
-        auto& [applyToken, transactionQueue] = *it;
-        while (!transactionQueue.empty()) {
-            if (stopTransactionProcessing(applyTokensWithUnsignaledTransactions)) {
-                ATRACE_NAME("stopTransactionProcessing");
-                break;
+TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
+        const TransactionHandler::TransactionFlushState& flushState) {
+    using TransactionReadiness = TransactionHandler::TransactionReadiness;
+    auto ready = TransactionReadiness::Ready;
+    flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s,
+                                                                   const std::shared_ptr<
+                                                                           renderengine::
+                                                                                   ExternalTexture>&
+                                                                           externalTexture)
+                                                                       -> bool {
+        sp<Layer> layer = LayerHandle::getLayer(s.surface);
+        const auto& transaction = *flushState.transaction;
+        // check for barrier frames
+        if (s.bufferData->hasBarrier) {
+            // The current producerId is already a newer producer than the buffer that has a
+            // barrier. This means the incoming buffer is older and we can release it here. We
+            // don't wait on the barrier since we know that's stale information.
+            if (layer->getDrawingState().barrierProducerId > s.bufferData->producerId) {
+                layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener,
+                                                 externalTexture->getBuffer(),
+                                                 s.bufferData->frameNumber,
+                                                 s.bufferData->acquireFence);
+                // Delete the entire state at this point and not just release the buffer because
+                // everything associated with the Layer in this Transaction is now out of date.
+                ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+                              layer->getDebugName(), layer->getDrawingState().barrierProducerId,
+                              s.bufferData->producerId);
+                return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
             }
 
-            auto& transaction = transactionQueue.front();
-            const auto ready =
-                transactionIsReadyToBeApplied(transaction,
-                                              transaction.frameTimelineInfo,
-                                              transaction.isAutoTimestamp,
-                                              transaction.desiredPresentTime,
-                                              transaction.originUid, transaction.states,
-                                              bufferLayersReadyToPresent, transactions.size(),
-                                              tryApplyUnsignaled);
-            ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-            if (ready == TransactionReadiness::NotReady) {
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            if (ready == TransactionReadiness::NotReadyBarrier) {
-                transactionsPendingBarrier++;
-                setTransactionFlags(eTransactionFlushNeeded);
-                break;
-            }
-            transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                const bool frameNumberChanged = state.bufferData->flags.test(
-                        BufferData::BufferDataChange::frameNumberChanged);
-                if (frameNumberChanged) {
-                    bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
-                } else {
-                    // Barrier function only used for BBQ which always includes a frame number
-                    bufferLayersReadyToPresent[state.surface] =
-                        std::numeric_limits<uint64_t>::max();
+            if (layer->getDrawingState().barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+                const bool willApplyBarrierFrame =
+                        flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+                        ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+                          s.bufferData->barrierFrameNumber));
+                if (!willApplyBarrierFrame) {
+                    ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
+                                  layer->getDebugName(),
+                                  layer->getDrawingState().barrierFrameNumber,
+                                  s.bufferData->barrierFrameNumber);
+                    ready = TransactionReadiness::NotReadyBarrier;
+                    return TraverseBuffersReturnValues::STOP_TRAVERSAL;
                 }
-            });
-            const bool appliedUnsignaled = (ready == TransactionReadiness::ReadyUnsignaled);
-            if (appliedUnsignaled) {
-                applyTokensWithUnsignaledTransactions.insert(transaction.applyToken);
             }
-
-            transactions.emplace_back(std::move(transaction));
-            transactionQueue.pop();
         }
 
-        if (transactionQueue.empty()) {
-            it = mPendingTransactionQueues.erase(it);
-            mTransactionQueueCV.broadcast();
-        } else {
-            it = std::next(it, 1);
+        // If backpressure is enabled and we already have a buffer to commit, keep
+        // the transaction in the queue.
+        const bool hasPendingBuffer =
+                flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+        if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
+            ATRACE_FORMAT("hasPendingBuffer %s", layer->getDebugName());
+            ready = TransactionReadiness::NotReady;
+            return TraverseBuffersReturnValues::STOP_TRAVERSAL;
         }
-    }
-    return transactionsPendingBarrier;
-}
 
-std::vector<TransactionState> SurfaceFlinger::flushTransactions() {
-    // to prevent onHandleDestroyed from being called while the lock is held,
-    // we must keep a copy of the transactions (specifically the composer
-    // states) around outside the scope of the lock
-    std::vector<TransactionState> transactions;
-    // Layer handles that have transactions with buffers that are ready to be applied.
-    std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>> bufferLayersReadyToPresent;
-    std::unordered_set<sp<IBinder>, SpHash<IBinder>> applyTokensWithUnsignaledTransactions;
-    {
-        Mutex::Autolock _l(mStateLock);
-        {
-            Mutex::Autolock _l(mQueueLock);
-
-            int lastTransactionsPendingBarrier = 0;
-            int transactionsPendingBarrier = 0;
-            // First collect transactions from the pending transaction queues.
-            // We are not allowing unsignaled buffers here as we want to
-            // collect all the transactions from applyTokens that are ready first.
-            transactionsPendingBarrier =
-                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                            applyTokensWithUnsignaledTransactions, /*tryApplyUnsignaled*/ false);
-
-            // Second, collect transactions from the transaction queue.
-            // Here as well we are not allowing unsignaled buffers for the same
-            // reason as above.
-            while (!mTransactionQueue.empty()) {
-                auto& transaction = mTransactionQueue.front();
-                const bool pendingTransactions =
-                        mPendingTransactionQueues.find(transaction.applyToken) !=
-                        mPendingTransactionQueues.end();
-                const auto ready = [&]() REQUIRES(mStateLock) {
-                    if (pendingTransactions) {
-                        ATRACE_NAME("pendingTransactions");
-                        return TransactionReadiness::NotReady;
-                    }
-
-                    return transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
-                                                         transaction.isAutoTimestamp,
-                                                         transaction.desiredPresentTime,
-                                                         transaction.originUid, transaction.states,
-                                                         bufferLayersReadyToPresent,
-                                                         transactions.size(),
-                                                         /*tryApplyUnsignaled*/ false);
-                }();
-                ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
-                if (ready != TransactionReadiness::Ready) {
-                    if (ready == TransactionReadiness::NotReadyBarrier) {
-                        transactionsPendingBarrier++;
-                    }
-                    mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
-                } else {
-                    transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
-                        const bool frameNumberChanged = state.bufferData->flags.test(
-                                BufferData::BufferDataChange::frameNumberChanged);
-                        if (frameNumberChanged) {
-                            bufferLayersReadyToPresent[state.surface] = state.bufferData->frameNumber;
-                        } else {
-                            // Barrier function only used for BBQ which always includes a frame number.
-                            // This value only used for barrier logic.
-                            bufferLayersReadyToPresent[state.surface] =
-                                std::numeric_limits<uint64_t>::max();
-                        }
-                    });
-                    transactions.emplace_back(std::move(transaction));
+        // ignore the acquire fence if LatchUnsignaledConfig::Always is set.
+        const bool checkAcquireFence = enableLatchUnsignaledConfig != LatchUnsignaledConfig::Always;
+        const bool acquireFenceAvailable = s.bufferData &&
+                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+                s.bufferData->acquireFence;
+        const bool fenceSignaled = !checkAcquireFence || !acquireFenceAvailable ||
+                s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+        if (!fenceSignaled) {
+            // check fence status
+            const bool allowLatchUnsignaled =
+                    shouldLatchUnsignaled(layer, s, transaction.states.size(),
+                                          flushState.firstTransaction);
+            if (allowLatchUnsignaled) {
+                ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+                              layer->getDebugName());
+                ready = TransactionReadiness::NotReadyUnsignaled;
+            } else {
+                ready = TransactionReadiness::NotReady;
+                auto& listener = s.bufferData->releaseBufferListener;
+                if (listener &&
+                    (flushState.queueProcessTime - transaction.postTime) >
+                            std::chrono::nanoseconds(4s).count()) {
+                    mTransactionHandler
+                            .onTransactionQueueStalled(transaction.id, listener,
+                                                       "Buffer processing hung up due to stuck "
+                                                       "fence. Indicates GPU hang");
                 }
-                mTransactionQueue.pop_front();
-                ATRACE_INT("TransactionQueue", mTransactionQueue.size());
-            }
-
-            // Transactions with a buffer pending on a barrier may be on a different applyToken
-            // than the transaction which satisfies our barrier. In fact this is the exact use case
-            // that the primitive is designed for. This means we may first process
-            // the barrier dependent transaction, determine it ineligible to complete
-            // and then satisfy in a later inner iteration of flushPendingTransactionQueues.
-            // The barrier dependent transaction was eligible to be presented in this frame
-            // but we would have prevented it without case. To fix this we continually
-            // loop through flushPendingTransactionQueues until we perform an iteration
-            // where the number of transactionsPendingBarrier doesn't change. This way
-            // we can continue to resolve dependency chains of barriers as far as possible.
-            while (lastTransactionsPendingBarrier != transactionsPendingBarrier) {
-                lastTransactionsPendingBarrier = transactionsPendingBarrier;
-                transactionsPendingBarrier =
-                    flushPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                        applyTokensWithUnsignaledTransactions,
-                        /*tryApplyUnsignaled*/ false);
-            }
-
-            // We collected all transactions that could apply without latching unsignaled buffers.
-            // If we are allowing latch unsignaled of some form, now it's the time to go over the
-            // transactions that were not applied and try to apply them unsignaled.
-            if (enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
-                flushUnsignaledPendingTransactionQueues(transactions, bufferLayersReadyToPresent,
-                                                        applyTokensWithUnsignaledTransactions);
+                ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
+                return TraverseBuffersReturnValues::STOP_TRAVERSAL;
             }
         }
-    }
-    return transactions;
+        return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+    });
+    return ready;
 }
 
-// for test only
-bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
-    std::vector<TransactionState> transactions = flushTransactions();
+void SurfaceFlinger::addTransactionReadyFilters() {
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyTimelineCheck, this, std::placeholders::_1));
+    mTransactionHandler.addTransactionReadyFilter(
+            std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
+}
+
+// For tests only
+bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
+    std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
     return applyTransactions(transactions, vsyncId);
 }
 
 bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
-                                       int64_t vsyncId) {
-    Mutex::Autolock _l(mStateLock);
+                                       VsyncId vsyncId) {
+    Mutex::Autolock lock(mStateLock);
     return applyTransactionsLocked(transactions, vsyncId);
 }
 
 bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions,
-                                             int64_t vsyncId) {
+                                             VsyncId vsyncId) {
     bool needsTraversal = false;
     // Now apply all transactions.
     for (auto& transaction : transactions) {
@@ -3951,50 +4423,40 @@
                                       transaction.displays, transaction.flags,
                                       transaction.inputWindowCommands,
                                       transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                      transaction.buffer, transaction.postTime,
-                                      transaction.permissions, transaction.hasListenerCallbacks,
+                                      std::move(transaction.uncacheBufferIds), transaction.postTime,
+                                      transaction.hasListenerCallbacks,
                                       transaction.listenerCallbacks, transaction.originPid,
                                       transaction.originUid, transaction.id);
-        if (transaction.transactionCommittedSignal) {
-            mTransactionCommittedSignals.emplace_back(
-                    std::move(transaction.transactionCommittedSignal));
-        }
-    }
-
-    if (mTransactionTracing) {
-        mTransactionTracing->addCommittedTransactions(transactions, vsyncId);
     }
     return needsTraversal;
 }
 
 bool SurfaceFlinger::transactionFlushNeeded() {
-    Mutex::Autolock _l(mQueueLock);
-    return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
+    return mTransactionHandler.hasPendingTransactions();
 }
 
-bool SurfaceFlinger::frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const {
-    // The amount of time SF can delay a frame if it is considered early based
-    // on the VsyncModulator::VsyncConfig::appWorkDuration
-    constexpr static std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
-
-    const auto currentVsyncPeriod = mScheduler->getDisplayStatInfo(systemTime()).vsyncPeriod;
-    const auto earlyLatchVsyncThreshold = currentVsyncPeriod / 2;
-
-    const auto prediction = mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId);
-    if (!prediction.has_value()) {
+bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId) const {
+    const auto prediction =
+            mFrameTimeline->getTokenManager()->getPredictionsForToken(vsyncId.value);
+    if (!prediction) {
         return false;
     }
 
-    if (std::abs(prediction->presentTime - expectedPresentTime) >=
-        kEarlyLatchMaxThreshold.count()) {
+    const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime);
+
+    if (std::chrono::abs(predictedPresentTime - expectedPresentTime) >=
+        scheduler::VsyncConfig::kEarlyLatchMaxThreshold) {
         return false;
     }
 
-    return prediction->presentTime >= expectedPresentTime &&
-            prediction->presentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
+    const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->period() / 2;
+
+    return predictedPresentTime >= expectedPresentTime &&
+            predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold;
 }
+
 bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t& state,
-                                           size_t numStates, size_t totalTXapplied) const {
+                                           size_t numStates, bool firstTransaction) const {
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
         ALOGV("%s: false (LatchUnsignaledConfig::Disabled)", __func__);
         return false;
@@ -4013,18 +4475,17 @@
     }
 
     if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::AutoSingleLayer) {
-        if (totalTXapplied > 0) {
-            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; totalTXapplied=%zu)",
-                  __func__, totalTXapplied);
+        if (!firstTransaction) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; not first transaction)",
+                  __func__);
             return false;
         }
 
         // We don't want to latch unsignaled if are in early / client composition
         // as it leads to jank due to RenderEngine waiting for unsignaled buffer
         // or window animations being slow.
-        const auto isDefaultVsyncConfig = mVsyncModulator->isVsyncConfigDefault();
-        if (!isDefaultVsyncConfig) {
-            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
+        if (mScheduler->vsyncModulator().isVsyncConfigEarly()) {
+            ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; isVsyncConfigEarly)",
                   __func__);
             return false;
         }
@@ -4039,116 +4500,107 @@
     return true;
 }
 
-auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction,
-        const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-        uid_t originUid, const Vector<ComposerState>& states,
-        const std::unordered_map<
-            sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-        size_t totalTXapplied, bool tryApplyUnsignaled) const -> TransactionReadiness {
-    ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
-    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
-    // Do not present if the desiredPresentTime has not passed unless it is more than one second
-    // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
-    if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
-        desiredPresentTime < expectedPresentTime + s2ns(1)) {
-        ATRACE_NAME("not current");
-        return TransactionReadiness::NotReady;
+status_t SurfaceFlinger::setTransactionState(
+        const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+        InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
+        const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+        const std::vector<uint64_t>& mergedTransactionIds) {
+    ATRACE_CALL();
+
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int originPid = ipc->getCallingPid();
+    const int originUid = ipc->getCallingUid();
+    uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
+    for (auto composerState : states) {
+        composerState.state.sanitize(permissions);
     }
 
-    if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
-        ATRACE_NAME("!isVsyncValid");
-        return TransactionReadiness::NotReady;
+    for (DisplayState display : displays) {
+        display.sanitize(permissions);
     }
 
-    // If the client didn't specify desiredPresentTime, use the vsyncId to determine the expected
-    // present time of this transaction.
-    if (isAutoTimestamp && frameIsEarly(expectedPresentTime, info.vsyncId)) {
-        ATRACE_NAME("frameIsEarly");
-        return TransactionReadiness::NotReady;
+    if (!inputWindowCommands.empty() &&
+        (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
+        ALOGE("Only privileged callers are allowed to send input commands.");
+        inputWindowCommands.clear();
     }
 
-    bool fenceUnsignaled = false;
-    auto queueProcessTime = systemTime();
-    for (const ComposerState& state : states) {
-        const layer_state_t& s = state.state;
-
-        sp<Layer> layer = nullptr;
-        if (s.surface) {
-            layer = fromHandle(s.surface).promote();
-        } else if (s.hasBufferChanges()) {
-            ALOGW("Transaction with buffer, but no Layer?");
-            continue;
-        }
-        if (!layer) {
-            continue;
-        }
-
-        if (s.hasBufferChanges() && s.bufferData->hasBarrier &&
-            ((layer->getDrawingState().frameNumber) < s.bufferData->barrierFrameNumber)) {
-            const bool willApplyBarrierFrame =
-                (bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end()) &&
-                (bufferLayersReadyToPresent.at(s.surface) >= s.bufferData->barrierFrameNumber);
-            if (!willApplyBarrierFrame) {
-                ATRACE_NAME("NotReadyBarrier");
-                return TransactionReadiness::NotReadyBarrier;
-            }
-        }
-
-        const bool allowLatchUnsignaled = tryApplyUnsignaled &&
-                shouldLatchUnsignaled(layer, s, states.size(), totalTXapplied);
-        ATRACE_FORMAT("%s allowLatchUnsignaled=%s", layer->getName().c_str(),
-                      allowLatchUnsignaled ? "true" : "false");
-
-        const bool acquireFenceChanged = s.bufferData &&
-                s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
-                s.bufferData->acquireFence;
-        fenceUnsignaled = fenceUnsignaled ||
-                (acquireFenceChanged &&
-                 s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
-
-        if (fenceUnsignaled && !allowLatchUnsignaled) {
-            if (!transaction.sentFenceTimeoutWarning &&
-                queueProcessTime - transaction.queueTime > std::chrono::nanoseconds(4s).count()) {
-                transaction.sentFenceTimeoutWarning = true;
-                auto listener = s.bufferData->releaseBufferListener;
-                if (listener) {
-                    listener->onTransactionQueueStalled();
-                }
-            }
-
-            ATRACE_NAME("fence unsignaled");
-            return TransactionReadiness::NotReady;
-        }
-
-        if (s.hasBufferChanges()) {
-            // If backpressure is enabled and we already have a buffer to commit, keep the
-            // transaction in the queue.
-            const bool hasPendingBuffer = bufferLayersReadyToPresent.find(s.surface) !=
-                bufferLayersReadyToPresent.end();
-            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
-                ATRACE_NAME("hasPendingBuffer");
-                return TransactionReadiness::NotReady;
-            }
+    if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+        const bool hasPermission =
+                (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
+                callingThreadHasPermission(sWakeupSurfaceFlinger);
+        if (!hasPermission) {
+            ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
+                  "eEarlyWakeup[Start|End] flags");
+            flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
         }
     }
-    return fenceUnsignaled ? TransactionReadiness::ReadyUnsignaled : TransactionReadiness::Ready;
-}
 
-void SurfaceFlinger::queueTransaction(TransactionState& state) {
-    state.queueTime = systemTime();
+    const int64_t postTime = systemTime();
 
-    Mutex::Autolock lock(mQueueLock);
-
-    // Generate a CountDownLatch pending state if this is a synchronous transaction.
-    if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
-        state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
-                (state.inputWindowCommands.syncInputWindows
-                         ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction)
-                         : CountDownLatch::eSyncTransaction));
+    std::vector<uint64_t> uncacheBufferIds;
+    uncacheBufferIds.reserve(uncacheBuffers.size());
+    for (const auto& uncacheBuffer : uncacheBuffers) {
+        sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+        if (buffer != nullptr) {
+            uncacheBufferIds.push_back(buffer->getId());
+        }
     }
 
-    mTransactionQueue.emplace_back(state);
-    ATRACE_INT("TransactionQueue", mTransactionQueue.size());
+    std::vector<ResolvedComposerState> resolvedStates;
+    resolvedStates.reserve(states.size());
+    for (auto& state : states) {
+        resolvedStates.emplace_back(std::move(state));
+        auto& resolvedState = resolvedStates.back();
+        if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
+            resolvedState.state.surface) {
+            sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
+            std::string layerName = (layer) ?
+                    layer->getDebugName() : std::to_string(resolvedState.state.layerId);
+            resolvedState.externalTexture =
+                    getExternalTextureFromBufferData(*resolvedState.state.bufferData,
+                                                     layerName.c_str(), transactionId);
+            mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
+        }
+        resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
+        if (resolvedState.state.what & layer_state_t::eReparent) {
+            resolvedState.parentId =
+                    getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
+        }
+        if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) {
+            resolvedState.relativeParentId =
+                    getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl);
+        }
+        if (resolvedState.state.what & layer_state_t::eInputInfoChanged) {
+            wp<IBinder>& touchableRegionCropHandle =
+                    resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle;
+            resolvedState.touchCropId =
+                    LayerHandle::getLayerId(touchableRegionCropHandle.promote());
+        }
+    }
+
+    TransactionState state{frameTimelineInfo,
+                           resolvedStates,
+                           displays,
+                           flags,
+                           applyToken,
+                           std::move(inputWindowCommands),
+                           desiredPresentTime,
+                           isAutoTimestamp,
+                           std::move(uncacheBufferIds),
+                           postTime,
+                           hasListenerCallbacks,
+                           listenerCallbacks,
+                           originPid,
+                           originUid,
+                           transactionId,
+                           mergedTransactionIds};
+
+    if (mTransactionTracing) {
+        mTransactionTracing->addQueuedTransaction(state);
+    }
 
     const auto schedule = [](uint32_t flags) {
         if (flags & eEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
@@ -4157,106 +4609,25 @@
     }(state.flags);
 
     const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
-
-    setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
-}
-
-void SurfaceFlinger::waitForSynchronousTransaction(
-        const CountDownLatch& transactionCommittedSignal) {
-    // applyTransactionState is called on the main SF thread.  While a given process may wish
-    // to wait on synchronous transactions, the main SF thread should apply the transaction and
-    // set the value to notify this after committed.
-    if (!transactionCommittedSignal.wait_until(
-                std::chrono::nanoseconds(mAnimationTransactionTimeout))) {
-        ALOGE("setTransactionState timed out!");
-    }
-}
-
-void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) {
-    for (auto it = mTransactionCommittedSignals.begin();
-         it != mTransactionCommittedSignals.end();) {
-        if ((*it)->countDown(flag)) {
-            it = mTransactionCommittedSignals.erase(it);
-        } else {
-            it++;
-        }
-    }
-}
-
-status_t SurfaceFlinger::setTransactionState(
-        const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
-        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-        const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
-    ATRACE_CALL();
-
-    uint32_t permissions =
-        callingThreadHasUnscopedSurfaceFlingerAccess() ?
-        layer_state_t::Permission::ACCESS_SURFACE_FLINGER : 0;
-    // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
-    // permissions.
-    if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
-        callingThreadHasRotateSurfaceFlingerAccess()) {
-        permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
-    }
-
-    if (callingThreadHasInternalSystemWindowAccess()) {
-        permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
-    }
-
-    if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) &&
-        (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
-        ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
-        flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
-    }
-
-    const int64_t postTime = systemTime();
-
-    IPCThreadState* ipc = IPCThreadState::self();
-    const int originPid = ipc->getCallingPid();
-    const int originUid = ipc->getCallingUid();
-    TransactionState state{frameTimelineInfo,  states,
-                           displays,           flags,
-                           applyToken,         inputWindowCommands,
-                           desiredPresentTime, isAutoTimestamp,
-                           uncacheBuffer,      postTime,
-                           permissions,        hasListenerCallbacks,
-                           listenerCallbacks,  originPid,
-                           originUid,          transactionId};
-
-    // Check for incoming buffer updates and increment the pending buffer count.
-    state.traverseStatesWithBuffers([&](const layer_state_t& state) {
-        mBufferCountTracker.increment(state.surface->localBinder());
-    });
-
-    if (mTransactionTracing) {
-        mTransactionTracing->addQueuedTransaction(state);
-    }
-    queueTransaction(state);
-
-    // Check the pending state to make sure the transaction is synchronous.
-    if (state.transactionCommittedSignal) {
-        waitForSynchronousTransaction(*state.transactionCommittedSignal);
-    }
-
+    mTransactionHandler.queueTransaction(std::move(state));
+    setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
     return NO_ERROR;
 }
 
 bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                                           Vector<ComposerState>& states,
+                                           std::vector<ResolvedComposerState>& states,
                                            Vector<DisplayState>& displays, uint32_t flags,
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
-                                           const client_cache_t& uncacheBuffer,
-                                           const int64_t postTime, uint32_t permissions,
-                                           bool hasListenerCallbacks,
+                                           const std::vector<uint64_t>& uncacheBufferIds,
+                                           const int64_t postTime, bool hasListenerCallbacks,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
-    for (DisplayState& display : displays) {
-        display.sanitize(permissions);
-        transactionFlags |= setDisplayStateLocked(display);
+    if (!mLayerLifecycleManagerEnabled) {
+        for (DisplayState& display : displays) {
+            transactionFlags |= setDisplayStateLocked(display);
+        }
     }
 
     // start and end registration for listeners w/ no surface so they can get their callback.  Note
@@ -4267,50 +4638,50 @@
     }
 
     uint32_t clientStateFlags = 0;
-    for (int i = 0; i < states.size(); i++) {
-        ComposerState& state = states.editItemAt(i);
-        clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
-                                                 isAutoTimestamp, postTime, permissions);
-        if ((flags & eAnimation) && state.state.surface) {
-            if (const auto layer = fromHandle(state.state.surface).promote()) {
-                using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
-                mScheduler->recordLayerHistory(layer.get(),
-                                               isAutoTimestamp ? 0 : desiredPresentTime,
-                                               LayerUpdateType::AnimationTX);
+    for (auto& resolvedState : states) {
+        if (mLegacyFrontEndEnabled) {
+            clientStateFlags |=
+                    setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
+                                         isAutoTimestamp, postTime, transactionId);
+
+        } else /*mLayerLifecycleManagerEnabled*/ {
+            clientStateFlags |= updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState,
+                                                             desiredPresentTime, isAutoTimestamp,
+                                                             postTime, transactionId);
+        }
+        if ((flags & eAnimation) && resolvedState.state.surface) {
+            if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
+                const auto layerProps = scheduler::LayerProps{
+                        .visible = layer->isVisible(),
+                        .bounds = layer->getBounds(),
+                        .transform = layer->getTransform(),
+                        .setFrameRateVote = layer->getFrameRateForLayerTree(),
+                        .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+                };
+                layer->recordLayerHistoryAnimationTx(layerProps);
             }
         }
     }
 
     transactionFlags |= clientStateFlags;
+    transactionFlags |= addInputWindowCommands(inputWindowCommands);
 
-    if (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) {
-        transactionFlags |= addInputWindowCommands(inputWindowCommands);
-    } else if (!inputWindowCommands.empty()) {
-        ALOGE("Only privileged callers are allowed to send input commands.");
-    }
-
-    if (uncacheBuffer.isValid()) {
-        ClientCache::getInstance().erase(uncacheBuffer);
+    for (uint64_t uncacheBufferId : uncacheBufferIds) {
+        mBufferIdsToUncache.push_back(uncacheBufferId);
     }
 
     // If a synchronous transaction is explicitly requested without any changes, force a transaction
     // anyway. This can be used as a flush mechanism for previous async transactions.
     // Empty animation transaction can be used to simulate back-pressure, so also force a
     // transaction for empty animation transactions.
-    if (transactionFlags == 0 &&
-            ((flags & eSynchronous) || (flags & eAnimation))) {
+    if (transactionFlags == 0 && (flags & eAnimation)) {
         transactionFlags = eTransactionNeeded;
     }
 
     bool needsTraversal = false;
     if (transactionFlags) {
-        if (mInterceptor->isEnabled()) {
-            mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
-                                          originPid, originUid, transactionId);
-        }
-
-        // We are on the main thread, we are about to preform a traversal. Clear the traversal bit
-        // so we don't have to wake up again next frame to preform an unnecessary traversal.
+        // We are on the main thread, we are about to perform a traversal. Clear the traversal bit
+        // so we don't have to wake up again next frame to perform an unnecessary traversal.
         if (transactionFlags & eTraversalNeeded) {
             transactionFlags = transactionFlags & (~eTraversalNeeded);
             needsTraversal = true;
@@ -4318,12 +4689,44 @@
         if (transactionFlags) {
             setTransactionFlags(transactionFlags);
         }
+    }
 
-        if (flags & eAnimation) {
-            mAnimTransactionPending = true;
+    return needsTraversal;
+}
+
+bool SurfaceFlinger::applyAndCommitDisplayTransactionStates(
+        std::vector<TransactionState>& transactions) {
+    Mutex::Autolock lock(mStateLock);
+    bool needsTraversal = false;
+    uint32_t transactionFlags = 0;
+    for (auto& transaction : transactions) {
+        for (DisplayState& display : transaction.displays) {
+            transactionFlags |= setDisplayStateLocked(display);
         }
     }
 
+    if (transactionFlags) {
+        // We are on the main thread, we are about to perform a traversal. Clear the traversal bit
+        // so we don't have to wake up again next frame to perform an unnecessary traversal.
+        if (transactionFlags & eTraversalNeeded) {
+            transactionFlags = transactionFlags & (~eTraversalNeeded);
+            needsTraversal = true;
+        }
+        if (transactionFlags) {
+            setTransactionFlags(transactionFlags);
+        }
+    }
+
+    mFrontEndDisplayInfosChanged = mTransactionFlags & eDisplayTransactionNeeded;
+    if (mFrontEndDisplayInfosChanged && !mLegacyFrontEndEnabled) {
+        processDisplayChangesLocked();
+        mFrontEndDisplayInfos.clear();
+        for (const auto& [_, display] : mDisplays) {
+            mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo());
+        }
+        needsTraversal = true;
+    }
+
     return needsTraversal;
 }
 
@@ -4394,11 +4797,10 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
-                                              ComposerState& composerState,
+                                              ResolvedComposerState& composerState,
                                               int64_t desiredPresentTime, bool isAutoTimestamp,
-                                              int64_t postTime, uint32_t permissions) {
+                                              int64_t postTime, uint64_t transactionId) {
     layer_state_t& s = composerState.state;
-    s.sanitize(permissions);
 
     std::vector<ListenerCallbacks> filteredListeners;
     for (auto& listener : s.listeners) {
@@ -4422,20 +4824,24 @@
     uint32_t flags = 0;
     sp<Layer> layer = nullptr;
     if (s.surface) {
-        layer = fromHandle(s.surface).promote();
+        layer = LayerHandle::getLayer(s.surface);
     } else {
         // The client may provide us a null handle. Treat it as if the layer was removed.
         ALOGW("Attempt to set client state with a null layer handle");
     }
     if (layer == nullptr) {
         for (auto& [listener, callbackIds] : s.listeners) {
-            mTransactionCallbackInvoker.registerUnpresentedCallbackHandle(
-                    new CallbackHandle(listener, callbackIds, s.surface));
+            mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
+                                                                                   callbackIds,
+                                                                                   s.surface),
+                                                          std::vector<JankData>());
         }
         return 0;
     }
     MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
 
+    ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+
     // Only set by BLAST adapter layers
     if (what & layer_state_t::eProducerDisconnect) {
         layer->onDisconnect();
@@ -4485,18 +4891,11 @@
             }
         }
     }
-    if (what & layer_state_t::eSizeChanged) {
-        if (layer->setSize(s.w, s.h)) {
-            flags |= eTraversalNeeded;
-        }
-    }
     if (what & layer_state_t::eAlphaChanged) {
-        if (layer->setAlpha(s.alpha))
-            flags |= eTraversalNeeded;
+        if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorChanged) {
-        if (layer->setColor(s.color))
-            flags |= eTraversalNeeded;
+        if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorTransformChanged) {
         if (layer->setColorTransform(s.colorTransform)) {
@@ -4504,7 +4903,7 @@
         }
     }
     if (what & layer_state_t::eBackgroundColorChanged) {
-        if (layer->setBackgroundColor(s.color, s.bgColorAlpha, s.bgColorDataspace)) {
+        if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4528,6 +4927,11 @@
     if (what & layer_state_t::eBlurRegionsChanged) {
         if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eRenderBorderChanged) {
+        if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
@@ -4548,8 +4952,8 @@
             flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
         }
     }
-    if (what & layer_state_t::eTransformChanged) {
-        if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
+    if (what & layer_state_t::eBufferTransformChanged) {
+        if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eTransformToDisplayInverseChanged) {
         if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
@@ -4561,9 +4965,6 @@
     if (what & layer_state_t::eDataspaceChanged) {
         if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
     }
-    if (what & layer_state_t::eHdrMetadataChanged) {
-        if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
-    }
     if (what & layer_state_t::eSurfaceDamageRegionChanged) {
         if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
     }
@@ -4579,16 +4980,20 @@
     }
     std::optional<nsecs_t> dequeueBufferTimestamp;
     if (what & layer_state_t::eMetadataChanged) {
-        dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
+        dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
 
-        if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) {
+        if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
+            gameMode != -1) {
             // The transaction will be received on the Task layer and needs to be applied to all
             // child layers. Child layers that are added at a later point will obtain the game mode
             // info through addChild().
             layer->setGameModeForTree(static_cast<GameMode>(gameMode));
         }
 
-        if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
+        if (layer->setMetadata(s.metadata)) {
+            flags |= eTraversalNeeded;
+            mLayerMetadataSnapshotNeeded = true;
+        }
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
         if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
@@ -4598,6 +5003,14 @@
     if (what & layer_state_t::eShadowRadiusChanged) {
         if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+        const auto compatibility =
+                Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility);
+
+        if (layer->setDefaultFrameRateCompatibility(compatibility)) {
+            flags |= eTraversalNeeded;
+        }
+    }
     if (what & layer_state_t::eFrameRateSelectionPriority) {
         if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
             flags |= eTraversalNeeded;
@@ -4625,6 +5038,19 @@
     if (what & layer_state_t::eDimmingEnabledChanged) {
         if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
+        if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eCachingHintChanged) {
+        if (layer->setCachingHint(s.cachingHint)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eHdrMetadataChanged) {
+        if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eTrustedOverlayChanged) {
         if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
             flags |= eTraversalNeeded;
@@ -4648,7 +5074,7 @@
     if (what & layer_state_t::eDropInputModeChanged) {
         if (layer->setDropInputMode(s.dropInputMode)) {
             flags |= eTraversalNeeded;
-            mInputInfoChanged = true;
+            mUpdateInputInfo = true;
         }
     }
     // This has to happen after we reparent children because when we reparent to null we remove
@@ -4671,24 +5097,156 @@
     std::vector<sp<CallbackHandle>> callbackHandles;
     if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
         for (auto& [listener, callbackIds] : filteredListeners) {
-            callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
+            callbackHandles.emplace_back(
+                    sp<CallbackHandle>::make(listener, callbackIds, s.surface));
         }
     }
 
     if (what & layer_state_t::eBufferChanged) {
-        std::shared_ptr<renderengine::ExternalTexture> buffer =
-                getExternalTextureFromBufferData(*s.bufferData, layer->getDebugName());
-        if (layer->setBuffer(buffer, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
-                             dequeueBufferTimestamp, frameTimelineInfo)) {
+        if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
+                             desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
+                             frameTimelineInfo)) {
             flags |= eTraversalNeeded;
         }
     } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
         layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
     }
 
-    if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
+    if ((what & layer_state_t::eBufferChanged) == 0) {
+        layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+    }
+
+    if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+        if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+                                              s.trustedPresentationListener)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+
+    if (what & layer_state_t::eFlushJankData) {
+        // Do nothing. Processing the transaction completed listeners currently cause the flush.
+    }
+
+    if (layer->setTransactionCompletedListeners(callbackHandles,
+                                                layer->willPresentCurrentTransaction() ||
+                                                        layer->willReleaseBufferOnLatch())) {
+        flags |= eTraversalNeeded;
+    }
+
     // Do not put anything that updates layer state or modifies flags after
     // setTransactionCompletedListener
+
+    // if the layer has been parented on to a new display, update its transform hint.
+    if (((flags & eTransformHintUpdateNeeded) == 0) &&
+        oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) {
+        flags |= eTransformHintUpdateNeeded;
+    }
+
+    return flags;
+}
+
+uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& frameTimelineInfo,
+                                                      ResolvedComposerState& composerState,
+                                                      int64_t desiredPresentTime,
+                                                      bool isAutoTimestamp, int64_t postTime,
+                                                      uint64_t transactionId) {
+    layer_state_t& s = composerState.state;
+
+    std::vector<ListenerCallbacks> filteredListeners;
+    for (auto& listener : s.listeners) {
+        // Starts a registration but separates the callback ids according to callback type. This
+        // allows the callback invoker to send on latch callbacks earlier.
+        // note that startRegistration will not re-register if the listener has
+        // already be registered for a prior surface control
+
+        ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
+        if (!onCommitCallbacks.callbackIds.empty()) {
+            filteredListeners.push_back(onCommitCallbacks);
+        }
+
+        ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
+        if (!onCompleteCallbacks.callbackIds.empty()) {
+            filteredListeners.push_back(onCompleteCallbacks);
+        }
+    }
+
+    const uint64_t what = s.what;
+    uint32_t flags = 0;
+    sp<Layer> layer = nullptr;
+    if (s.surface) {
+        layer = LayerHandle::getLayer(s.surface);
+    } else {
+        // The client may provide us a null handle. Treat it as if the layer was removed.
+        ALOGW("Attempt to set client state with a null layer handle");
+    }
+    if (layer == nullptr) {
+        for (auto& [listener, callbackIds] : s.listeners) {
+            mTransactionCallbackInvoker.addCallbackHandle(sp<CallbackHandle>::make(listener,
+                                                                                   callbackIds,
+                                                                                   s.surface),
+                                                          std::vector<JankData>());
+        }
+        return 0;
+    }
+    if (what & layer_state_t::eProducerDisconnect) {
+        layer->onDisconnect();
+    }
+    std::optional<nsecs_t> dequeueBufferTimestamp;
+    if (what & layer_state_t::eMetadataChanged) {
+        dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
+    }
+
+    std::vector<sp<CallbackHandle>> callbackHandles;
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
+        for (auto& [listener, callbackIds] : filteredListeners) {
+            callbackHandles.emplace_back(
+                    sp<CallbackHandle>::make(listener, callbackIds, s.surface));
+        }
+    }
+    // TODO(b/238781169) remove after screenshot refactor, currently screenshots
+    // requires to read drawing state from binder thread. So we need to fix that
+    // before removing this.
+    if (what & layer_state_t::eCropChanged) {
+        if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eSidebandStreamChanged) {
+        if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eBufferChanged) {
+        std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
+        frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
+        if (snapshot) {
+            transformHint = snapshot->transformHint;
+        }
+        layer->setTransformHint(transformHint);
+        if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
+                             desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
+                             frameTimelineInfo)) {
+            flags |= eTraversalNeeded;
+        }
+        mLayersWithQueuedFrames.emplace(layer);
+    } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+        layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
+    }
+
+    if ((what & layer_state_t::eBufferChanged) == 0) {
+        layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
+    }
+
+    if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+        if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+                                              s.trustedPresentationListener)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+
+    const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence());
+    bool willPresentCurrentTransaction = requestedLayerState &&
+            (requestedLayerState->hasReadyFrame() ||
+             requestedLayerState->willReleaseBufferOnLatch());
+    if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction))
+        flags |= eTraversalNeeded;
+
     return flags;
 }
 
@@ -4698,65 +5256,104 @@
 }
 
 status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
-                                     const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
-                                     int32_t* outLayerId) {
+                                     const sp<IBinder>& mirrorFromHandle,
+                                     gui::CreateSurfaceResult& outResult) {
     if (!mirrorFromHandle) {
         return NAME_NOT_FOUND;
     }
 
     sp<Layer> mirrorLayer;
     sp<Layer> mirrorFrom;
+    LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
     {
         Mutex::Autolock _l(mStateLock);
-        mirrorFrom = fromHandle(mirrorFromHandle).promote();
+        mirrorFrom = LayerHandle::getLayer(mirrorFromHandle);
         if (!mirrorFrom) {
             return NAME_NOT_FOUND;
         }
-        status_t result = createContainerLayer(args, outHandle, &mirrorLayer);
+        mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
+        mirrorArgs.mirrorLayerHandle = mirrorFromHandle;
+        mirrorArgs.addToRoot = false;
+        status_t result = createEffectLayer(mirrorArgs, &outResult.handle, &mirrorLayer);
         if (result != NO_ERROR) {
             return result;
         }
 
-        mirrorLayer->setClonedChild(mirrorFrom->createClone());
+        mirrorLayer->setClonedChild(mirrorFrom->createClone(mirrorLayer->getSequence()));
     }
 
-    *outLayerId = mirrorLayer->sequence;
-    if (mTransactionTracing) {
-        mTransactionTracing->onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
-                                                args.name, mirrorFrom->sequence);
-    }
-    return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
-                          false /* addToRoot */, nullptr /* outTransformHint */);
+    outResult.layerId = mirrorLayer->sequence;
+    outResult.layerName = String16(mirrorLayer->getDebugName());
+    return addClientLayer(mirrorArgs, outResult.handle, mirrorLayer /* layer */,
+                          nullptr /* parent */, nullptr /* outTransformHint */);
 }
 
-status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
-                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
-                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
-    ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
-            "Expected only one of parentLayer or parentHandle to be non-null. "
-            "Programmer error?");
+status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
+                                       gui::CreateSurfaceResult& outResult) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int uid = ipc->getCallingUid();
+    if (uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM && uid != AID_SHELL) {
+        ALOGE("Permission denied when trying to mirror display");
+        return PERMISSION_DENIED;
+    }
 
+    ui::LayerStack layerStack;
+    sp<Layer> rootMirrorLayer;
+    status_t result = 0;
+
+    {
+        Mutex::Autolock lock(mStateLock);
+
+        const auto display = getDisplayDeviceLocked(displayId);
+        if (!display) {
+            return NAME_NOT_FOUND;
+        }
+
+        layerStack = display->getLayerStack();
+        LayerCreationArgs mirrorArgs = LayerCreationArgs::fromOtherArgs(args);
+        mirrorArgs.flags |= ISurfaceComposerClient::eNoColorFill;
+        mirrorArgs.addToRoot = true;
+        mirrorArgs.layerStackToMirror = layerStack;
+        result = createEffectLayer(mirrorArgs, &outResult.handle, &rootMirrorLayer);
+        outResult.layerId = rootMirrorLayer->sequence;
+        outResult.layerName = String16(rootMirrorLayer->getDebugName());
+        result |= addClientLayer(mirrorArgs, outResult.handle, rootMirrorLayer /* layer */,
+                                 nullptr /* parent */, nullptr /* outTransformHint */);
+    }
+
+    if (result != NO_ERROR) {
+        return result;
+    }
+
+    if (mLegacyFrontEndEnabled) {
+        std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
+        mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
+    }
+
+    setTransactionFlags(eTransactionFlushNeeded);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult) {
     status_t result = NO_ERROR;
 
     sp<Layer> layer;
 
     switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-        case ISurfaceComposerClient::eFXSurfaceBufferState: {
-            result = createBufferStateLayer(args, outHandle, &layer);
+        case ISurfaceComposerClient::eFXSurfaceContainer:
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            args.flags |= ISurfaceComposerClient::eNoColorFill;
+            FMT_FALLTHROUGH;
+        case ISurfaceComposerClient::eFXSurfaceEffect: {
+            result = createBufferStateLayer(args, &outResult.handle, &layer);
             std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
             if (pendingBufferCounter) {
                 std::string counterName = layer->getPendingBufferCounterName();
-                mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
+                mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
                                         pendingBufferCounter);
             }
         } break;
-        case ISurfaceComposerClient::eFXSurfaceEffect:
-            result = createEffectLayer(args, outHandle, &layer);
-            break;
-        case ISurfaceComposerClient::eFXSurfaceContainer:
-            result = createContainerLayer(args, outHandle, &layer);
-            break;
         default:
             result = BAD_VALUE;
             break;
@@ -4766,73 +5363,27 @@
         return result;
     }
 
-    bool addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
-    wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
-    if (parentHandle != nullptr && parent == nullptr) {
-        ALOGE("Invalid parent handle %p.", parentHandle.get());
-        addToRoot = false;
-    }
-    if (parentLayer != nullptr) {
-        addToRoot = false;
+    args.addToRoot = args.addToRoot && callingThreadHasUnscopedSurfaceFlingerAccess();
+    // We can safely promote the parent layer in binder thread because we have a strong reference
+    // to the layer's handle inside this scope.
+    sp<Layer> parent = LayerHandle::getLayer(args.parentHandle.promote());
+    if (args.parentHandle != nullptr && parent == nullptr) {
+        ALOGE("Invalid parent handle %p", args.parentHandle.promote().get());
+        args.addToRoot = false;
     }
 
-    int parentId = -1;
-    // We can safely promote the layer in binder thread because we have a strong reference
-    // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
-    sp<Layer> parentSp = parent.promote();
-    if (parentSp != nullptr) {
-        parentId = parentSp->getSequence();
-    }
-    if (mTransactionTracing) {
-        mTransactionTracing->onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
-                                          args.flags, parentId);
-    }
-
-    result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
+    uint32_t outTransformHint;
+    result = addClientLayer(args, outResult.handle, layer, parent, &outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
 
-    *outLayerId = layer->sequence;
+    outResult.transformHint = static_cast<int32_t>(outTransformHint);
+    outResult.layerId = layer->sequence;
+    outResult.layerName = String16(layer->getDebugName());
     return result;
 }
 
-status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
-                                                sp<IBinder>* handle,
-                                                sp<IGraphicBufferProducer>* gbp,
-                                                sp<Layer>* outLayer) {
-    // initialize the surfaces
-    switch (format) {
-    case PIXEL_FORMAT_TRANSPARENT:
-    case PIXEL_FORMAT_TRANSLUCENT:
-        format = PIXEL_FORMAT_RGBA_8888;
-        break;
-    case PIXEL_FORMAT_OPAQUE:
-        format = PIXEL_FORMAT_RGBX_8888;
-        break;
-    }
-
-    sp<BufferQueueLayer> layer;
-    args.textureName = getNewTexture();
-    {
-        // Grab the SF state lock during this since it's the only safe way to access
-        // RenderEngine when creating a BufferLayerConsumer
-        // TODO: Check if this lock is still needed here
-        Mutex::Autolock lock(mStateLock);
-        layer = getFactory().createBufferQueueLayer(args);
-    }
-
-    status_t err = layer->setDefaultBufferProperties(0, 0, format);
-    if (err == NO_ERROR) {
-        *handle = layer->getHandle();
-        *gbp = layer->getProducer();
-        *outLayer = layer;
-    }
-
-    ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err));
-    return err;
-}
-
 status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
                                                 sp<Layer>* outLayer) {
     args.textureName = getNewTexture();
@@ -4848,40 +5399,45 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
-                                              sp<Layer>* outLayer) {
-    *outLayer = getFactory().createContainerLayer(args);
-    *handle = (*outLayer)->getHandle();
-    return NO_ERROR;
-}
-
 void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
     mLayersPendingRemoval.add(layer);
     mLayersRemoved = true;
     setTransactionFlags(eTransactionNeeded);
 }
 
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) {
+void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
+    {
+        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+        mDestroyedHandles.emplace_back(layerId);
+    }
+
     Mutex::Autolock lock(mStateLock);
     markLayerPendingRemovalLocked(layer);
+    layer->onHandleDestroyed();
     mBufferCountTracker.remove(handle);
     layer.clear();
-    if (mTransactionTracing) {
-        mTransactionTracing->onHandleRemoved(handle);
-    }
+
+    setTransactionFlags(eTransactionFlushNeeded);
 }
 
-// ---------------------------------------------------------------------------
-
-void SurfaceFlinger::onInitializeDisplays() {
-    const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::initializeDisplays() {
+    const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
     if (!display) return;
 
     const sp<IBinder> token = display->getDisplayToken().promote();
     LOG_ALWAYS_FATAL_IF(token == nullptr);
 
+    TransactionState state;
+    state.inputWindowCommands = mInputWindowCommands;
+    const nsecs_t now = systemTime();
+    state.desiredPresentTime = now;
+    state.postTime = now;
+    state.originPid = mPid;
+    state.originUid = static_cast<int>(getuid());
+    const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++;
+    state.id = transactionId;
+
     // reset screen orientation and use primary layer stack
-    Vector<ComposerState> state;
     Vector<DisplayState> displays;
     DisplayState d;
     d.what = DisplayState::eDisplayProjectionChanged |
@@ -4893,30 +5449,21 @@
     d.layerStackSpaceRect.makeInvalid();
     d.width = 0;
     d.height = 0;
-    displays.add(d);
+    state.displays.add(d);
 
-    nsecs_t now = systemTime();
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back(state);
 
-    int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
-    // It should be on the main thread, apply it directly.
-    applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
-                          /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
-                          {}, mPid, getuid(), transactionId);
+    if (mLegacyFrontEndEnabled) {
+        applyTransactions(transactions, VsyncId{0});
+    } else {
+        applyAndCommitDisplayTransactionStates(transactions);
+    }
 
-    setPowerModeInternal(display, hal::PowerMode::ON);
-    const nsecs_t vsyncPeriod = display->refreshRateConfigs().getActiveMode()->getVsyncPeriod();
-    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
-    mActiveDisplayTransformHint = display->getTransformHint();
-    // Use phase of 0 since phase is not known.
-    // Use latency of 0, which will snap to the ideal latency.
-    DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod};
-    setCompositorTimingSnapped(stats, 0);
-}
-
-void SurfaceFlinger::initializeDisplays() {
-    // Async since we may be called from the main thread.
-    static_cast<void>(
-            mScheduler->schedule([this]() FTL_FAKE_GUARD(mStateLock) { onInitializeDisplays(); }));
+    {
+        ftl::FakeGuard guard(mStateLock);
+        setPowerModeInternal(display, hal::PowerMode::ON);
+    }
 }
 
 void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4928,61 +5475,83 @@
     const auto displayId = display->getPhysicalId();
     ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str());
 
-    std::optional<hal::PowerMode> currentMode = display->getPowerMode();
-    if (currentMode.has_value() && mode == *currentMode) {
+    const auto currentModeOpt = display->getPowerMode();
+    if (currentModeOpt == mode) {
         return;
     }
 
-    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayToken);
-    if (activeDisplay != display && display->isInternal() && activeDisplay &&
-        activeDisplay->isPoweredOn()) {
-        ALOGW("Trying to change power mode on non active display while the active display is ON");
-    }
+    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                           .transform(&PhysicalDisplay::isInternal)
+                                           .value_or(false);
+
+    const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
+
+    ALOGW_IF(display != activeDisplay && isInternalDisplay && activeDisplay &&
+                     activeDisplay->isPoweredOn(),
+             "Trying to change power mode on inactive display without powering off active display");
 
     display->setPowerMode(mode);
 
-    if (mInterceptor->isEnabled()) {
-        mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
-    }
-    const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
-    if (!currentMode || *currentMode == hal::PowerMode::OFF) {
+    const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
+    if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
         // Turn on the display
-        if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) {
-            onActiveDisplayChangedLocked(display);
+
+        // Activate the display (which involves a modeset to the active mode) when the inner or
+        // outer display of a foldable is powered on. This condition relies on the above
+        // DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the same display,
+        // then the `activeDisplay->isPoweredOn()` below is true, such that the display is not
+        // activated every time it is powered on.
+        //
+        // TODO(b/255635821): Remove the concept of active display.
+        if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
+            onActiveDisplayChangedLocked(activeDisplay.get(), *display);
         }
-        // Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
-        // We can merge the syscall later.
-        if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
-            ALOGW("Couldn't set uclamp.min on display on: %s\n", strerror(errno));
+
+        if (displayId == mActiveDisplayId) {
+            // TODO(b/281692563): Merge the syscalls. For now, keep uclamp in a separate syscall and
+            // set it before SCHED_FIFO due to b/190237315.
+            if (setSchedAttr(true) != NO_ERROR) {
+                ALOGW("Failed to set uclamp.min after powering on active display: %s",
+                      strerror(errno));
+            }
+            if (setSchedFifo(true) != NO_ERROR) {
+                ALOGW("Failed to set SCHED_FIFO after powering on active display: %s",
+                      strerror(errno));
+            }
         }
-        if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
-            ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
-        }
+
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && mode != hal::PowerMode::DOZE_SUSPEND) {
-            setHWCVsyncEnabled(displayId, mHWCVsyncPendingState);
-            mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, refreshRate);
+        if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
+            setHWCVsyncEnabled(displayId,
+                               mScheduler->getVsyncSchedule(displayId)
+                                       ->getPendingHardwareVsyncState());
+            mScheduler->enableSyntheticVsync(false);
+            mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
         }
 
         mVisibleRegionsDirty = true;
-        mHasPoweredOff = true;
         scheduleComposite(FrameHint::kActive);
     } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
-        if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
-            ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
-        }
-        if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) {
-            ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno));
-        }
-        if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) {
-            mScheduler->disableHardwareVsync(true);
-            mScheduler->onScreenReleased(mAppConnectionHandle);
+
+        if (displayId == mActiveDisplayId) {
+            if (setSchedFifo(false) != NO_ERROR) {
+                ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+                      strerror(errno));
+            }
+            if (setSchedAttr(false) != NO_ERROR) {
+                ALOGW("Failed set uclamp.min after powering off active display: %s",
+                      strerror(errno));
+            }
+
+            if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+                mScheduler->disableHardwareVsync(displayId, true);
+                mScheduler->enableSyntheticVsync();
+            }
         }
 
         // Make sure HWVsync is disabled before turning off the display
-        setHWCVsyncEnabled(displayId, hal::Vsync::DISABLE);
+        setHWCVsyncEnabled(displayId, false);
 
         getHwComposer().setPowerMode(displayId, mode);
         mVisibleRegionsDirty = true;
@@ -4990,15 +5559,18 @@
     } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(displayId, mode);
-        if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) {
-            mScheduler->onScreenAcquired(mAppConnectionHandle);
-            mScheduler->resyncToHardwareVsync(true, refreshRate);
+        if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
+            ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
+            mVisibleRegionsDirty = true;
+            scheduleRepaint();
+            mScheduler->enableSyntheticVsync(false);
+            mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate);
         }
     } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
-        if (isDisplayActiveLocked(display)) {
-            mScheduler->disableHardwareVsync(true);
-            mScheduler->onScreenReleased(mAppConnectionHandle);
+        if (displayId == mActiveDisplayId) {
+            mScheduler->disableHardwareVsync(displayId, true);
+            mScheduler->enableSyntheticVsync();
         }
         getHwComposer().setPowerMode(displayId, mode);
     } else {
@@ -5006,17 +5578,18 @@
         getHwComposer().setPowerMode(displayId, mode);
     }
 
-    if (isDisplayActiveLocked(display)) {
+    if (displayId == mActiveDisplayId) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
-        mScheduler->setDisplayPowerMode(mode);
+        mScheduler->setDisplayPowerMode(displayId, mode);
     }
 
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+                                               kMainThreadContext) {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -5047,17 +5620,18 @@
                 {"--comp-displays"s, dumper(&SurfaceFlinger::dumpCompositionDisplays)},
                 {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
                 {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
-                {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
                 {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
+                {"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
+                {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+                {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
                 {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
                 {"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
-                {"--static-screen"s, dumper(&SurfaceFlinger::dumpStaticScreenStats)},
+                {"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
                 {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
-                {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)},
+                {"--vsync"s, dumper(&SurfaceFlinger::dumpVsync)},
                 {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)},
-                {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
         };
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
@@ -5102,7 +5676,8 @@
             LayersTraceProto* layersTrace = traceFileProto.add_entry();
             LayersProto layersProto = dumpProtoFromMainThread();
             layersTrace->mutable_layers()->Swap(&layersProto);
-            dumpDisplayProto(*layersTrace);
+            auto displayProtos = dumpDisplayProto();
+            layersTrace->mutable_displays()->Swap(&displayProtos);
 
             if (asProto) {
                 result.append(traceFileProto.SerializeAsString());
@@ -5120,13 +5695,6 @@
 }
 
 status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
-    if (asProto) {
-        mLayerTracing.writeToFile();
-        if (mTransactionTracing) {
-            mTransactionTracing->writeToFile();
-        }
-    }
-
     return doDump(fd, DumpArgs(), asProto);
 }
 
@@ -5137,17 +5705,14 @@
 
 void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
     StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
+    if (args.size() < 2) return;
 
-    if (args.size() > 1) {
-        const auto name = String8(args[1]);
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
-            if (layer->getName() == name.c_str()) {
-                layer->dumpFrameStats(result);
-            }
-        });
-    } else {
-        mAnimFrameTracker.dumpStats(result);
-    }
+    const auto name = String8(args[1]);
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+        if (layer->getName() == name.c_str()) {
+            layer->dumpFrameStats(result);
+        }
+    });
 }
 
 void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
@@ -5159,8 +5724,6 @@
             layer->clearFrameStats();
         }
     });
-
-    mAnimFrameTracker.clearStats();
 }
 
 void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const {
@@ -5171,12 +5734,13 @@
     mFrameTimeline->parseArgs(args, result);
 }
 
-void SurfaceFlinger::logFrameStats() {
-    mDrawingState.traverse([&](Layer* layer) {
-        layer->logFrameStats();
-    });
+void SurfaceFlinger::logFrameStats(TimePoint now) {
+    static TimePoint sTimestamp = now;
+    if (now - sTimestamp < 30min) return;
+    sTimestamp = now;
 
-    mAnimFrameTracker.logAndResetStats("<win-anim>");
+    ATRACE_CALL();
+    mDrawingState.traverse([&](Layer* layer) { layer->logFrameStats(); });
 }
 
 void SurfaceFlinger::appendSfConfigString(std::string& result) const {
@@ -5192,24 +5756,31 @@
     result.append("]");
 }
 
-void SurfaceFlinger::dumpVSync(std::string& result) const {
-    mScheduler->dump(result);
+void SurfaceFlinger::dumpScheduler(std::string& result) const {
+    utils::Dumper dumper{result};
+
+    mScheduler->dump(dumper);
+
+    // TODO(b/241285876): Move to DisplayModeController.
+    dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
+    dumper.eol();
 
     mRefreshRateStats->dump(result);
-    result.append("\n");
+    dumper.eol();
 
     mVsyncConfiguration->dump(result);
     StringAppendF(&result,
-                  "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
+                  "         present offset: %9" PRId64 " ns\t        VSYNC period: %9" PRId64
+                  " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
+}
 
-    StringAppendF(&result, "(mode override by backdoor: %s)\n\n",
-                  mDebugDisplayModeSetByBackdoor ? "yes" : "no");
-
+void SurfaceFlinger::dumpEvents(std::string& result) const {
     mScheduler->dump(mAppConnectionHandle, result);
+}
+
+void SurfaceFlinger::dumpVsync(std::string& result) const {
     mScheduler->dumpVsync(result);
-    StringAppendF(&result, "mHWCVsyncPendingState=%s mLastHWCVsyncState=%s\n",
-                  to_string(mHWCVsyncPendingState).c_str(), to_string(mLastHWCVsyncState).c_str());
 }
 
 void SurfaceFlinger::dumpPlannerInfo(const DumpArgs& args, std::string& result) const {
@@ -5219,21 +5790,6 @@
     }
 }
 
-void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
-    result.append("Static screen stats:\n");
-    for (size_t b = 0; b < SurfaceFlingerBE::NUM_BUCKETS - 1; ++b) {
-        float bucketTimeSec = getBE().mFrameBuckets[b] / 1e9;
-        float percent = 100.0f *
-                static_cast<float>(getBE().mFrameBuckets[b]) / getBE().mTotalTime;
-        StringAppendF(&result, "  < %zd frames: %.3f s (%.1f%%)\n", b + 1, bucketTimeSec, percent);
-    }
-    float bucketTimeSec = getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] / 1e9;
-    float percent = 100.0f *
-            static_cast<float>(getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1]) / getBE().mTotalTime;
-    StringAppendF(&result, "  %zd+ frames: %.3f s (%.1f%%)\n", SurfaceFlingerBE::NUM_BUCKETS - 1,
-                  bucketTimeSec, percent);
-}
-
 void SurfaceFlinger::dumpCompositionDisplays(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         display->getCompositionDisplay()->dump(result);
@@ -5242,9 +5798,25 @@
 }
 
 void SurfaceFlinger::dumpDisplays(std::string& result) const {
+    utils::Dumper dumper{result};
+
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        utils::Dumper::Section section(dumper, ftl::Concat("Display ", id.value).str());
+
+        display.snapshot().dump(dumper);
+
+        if (const auto device = getDisplayDeviceLocked(id)) {
+            device->dump(dumper);
+        }
+    }
+
     for (const auto& [token, display] : mDisplays) {
-        display->dump(result);
-        result += '\n';
+        if (display->isVirtual()) {
+            const auto displayId = display->getId();
+            utils::Dumper::Section section(dumper,
+                                           ftl::Concat("Virtual Display ", displayId.value).str());
+            display->dump(dumper);
+        }
     }
 }
 
@@ -5299,44 +5871,60 @@
 }
 
 void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
-    StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay);
+    StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor);
     StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
     StringAppendF(&result, "DisplayColorSetting: %s\n",
                   decodeDisplayColorSetting(mDisplayColorSetting).c_str());
 
     // TODO: print out if wide-color mode is active or not
 
-    for (const auto& [token, display] : mDisplays) {
-        const auto displayId = PhysicalDisplayId::tryCast(display->getId());
-        if (!displayId) {
-            continue;
-        }
-
-        StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str());
-        std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId);
-        for (auto&& mode : modes) {
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        StringAppendF(&result, "Display %s color modes:\n", to_string(id).c_str());
+        for (const auto mode : display.snapshot().colorModes()) {
             StringAppendF(&result, "    %s (%d)\n", decodeColorMode(mode).c_str(), mode);
         }
 
-        ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
-        StringAppendF(&result, "    Current color mode: %s (%d)\n",
-                      decodeColorMode(currentMode).c_str(), currentMode);
+        if (const auto display = getDisplayDeviceLocked(id)) {
+            ui::ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
+            StringAppendF(&result, "    Current color mode: %s (%d)\n",
+                          decodeColorMode(currentMode).c_str(), currentMode);
+        }
     }
     result.append("\n");
 }
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
-    LayersProto layersProto;
-    for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags);
+    std::unordered_set<uint64_t> stackIdsToSkip;
+
+    // Determine if virtual layers display should be skipped
+    if ((traceFlags & LayerTracing::TRACE_VIRTUAL_DISPLAYS) == 0) {
+        for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
+            if (display->isVirtual()) {
+                stackIdsToSkip.insert(display->getLayerStack().id);
+            }
+        }
     }
 
-    return layersProto;
+    if (mLegacyFrontEndEnabled) {
+        LayersProto layersProto;
+        for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+            if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
+                continue;
+            }
+            layer->writeToProto(layersProto, traceFlags);
+        }
+        return layersProto;
+    }
+
+    return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
+                                           mLegacyLayers, traceFlags)
+            .generate(mLayerHierarchyBuilder.getHierarchy());
 }
 
-void SurfaceFlinger::dumpDisplayProto(LayersTraceProto& layersTraceProto) const {
+google::protobuf::RepeatedPtrField<DisplayProto> SurfaceFlinger::dumpDisplayProto() const {
+    google::protobuf::RepeatedPtrField<DisplayProto> displays;
     for (const auto& [_, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
-        DisplayProto* displayProto = layersTraceProto.add_displays();
+        DisplayProto* displayProto = displays.Add();
         displayProto->set_id(display->getId().value);
         displayProto->set_name(display->getDisplayName());
         displayProto->set_layer_stack(display->getLayerStack().id);
@@ -5349,6 +5937,7 @@
                                                 displayProto->mutable_transform());
         displayProto->set_is_virtual(display->isVirtual());
     }
+    return displays;
 }
 
 void SurfaceFlinger::dumpHwc(std::string& result) const {
@@ -5383,7 +5972,7 @@
         std::string result;
         for (Layer* offscreenLayer : mOffscreenLayers) {
             offscreenLayer->traverse(LayerVector::StateSet::Drawing,
-                                     [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+                                     [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
         }
         return result;
     });
@@ -5392,6 +5981,23 @@
     result.append(future.get());
 }
 
+void SurfaceFlinger::dumpHwcLayersMinidumpLocked(std::string& result) const {
+    for (const auto& [token, display] : mDisplays) {
+        const auto displayId = HalDisplayId::tryCast(display->getId());
+        if (!displayId) {
+            continue;
+        }
+
+        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
+                      displayId == mActiveDisplayId ? "active" : "inactive");
+        Layer::miniDumpHeader(result);
+
+        const DisplayDevice& ref = *display;
+        mDrawingState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
+        result.append("\n");
+    }
+}
+
 void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
                                    std::string& result) const {
     const bool colorize = !args.empty() && args[0] == String16("--color");
@@ -5427,10 +6033,9 @@
     colorizer.bold(result);
     result.append("Scheduler:\n");
     colorizer.reset(result);
-    dumpVSync(result);
-    result.append("\n");
-
-    dumpStaticScreenStats(result);
+    dumpScheduler(result);
+    dumpEvents(result);
+    dumpVsync(result);
     result.append("\n");
 
     StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load());
@@ -5475,17 +6080,15 @@
         StringAppendF(&result, "  orientation=%s, isPoweredOn=%d\n",
                       toCString(display->getOrientation()), display->isPoweredOn());
     }
-    StringAppendF(&result,
-                  "  transaction-flags         : %08x\n"
-                  "  gpu_to_cpu_unsupported    : %d\n",
-                  mTransactionFlags.load(), !mGpuToCpuSupported);
+    StringAppendF(&result, "  transaction-flags         : %08x\n", mTransactionFlags.load());
 
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         std::string fps, xDpi, yDpi;
-        if (const auto activeMode = display->getActiveMode()) {
-            fps = to_string(activeMode->getFps());
+        if (const auto activeModePtr =
+                    display->refreshRateSelector().getActiveMode().modePtr.get()) {
+            fps = to_string(activeModePtr->getFps());
 
-            const auto dpi = activeMode->getDpi();
+            const auto dpi = activeModePtr->getDpi();
             xDpi = base::StringPrintf("%.2f", dpi.x);
             yDpi = base::StringPrintf("%.2f", dpi.y);
         } else {
@@ -5516,23 +6119,7 @@
     }
     result.push_back('\n');
 
-    /*
-     * HWC layer minidump
-     */
-    for (const auto& [token, display] : mDisplays) {
-        const auto displayId = HalDisplayId::tryCast(display->getId());
-        if (!displayId) {
-            continue;
-        }
-
-        StringAppendF(&result, "Display %s (%s) HWC layers:\n", to_string(*displayId).c_str(),
-                      (isDisplayActiveLocked(display) ? "active" : "inactive"));
-        Layer::miniDumpHeader(result);
-
-        const DisplayDevice& ref = *display;
-        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
-        result.append("\n");
-    }
+    dumpHwcLayersMinidumpLocked(result);
 
     {
         DumpArgs plannerArgs;
@@ -5564,6 +6151,16 @@
 
     result.append(mTimeStats->miniDump());
     result.append("\n");
+
+    result.append("Window Infos:\n");
+    auto windowInfosDebug = mWindowInfosListenerInvoker->getDebugInfo();
+    StringAppendF(&result, "  max send vsync id: %" PRId64 "\n",
+                  windowInfosDebug.maxSendDelayVsyncId.value);
+    StringAppendF(&result, "  max send delay (ns): %" PRId64 " ns\n",
+                  windowInfosDebug.maxSendDelayDuration);
+    StringAppendF(&result, "  unsent messages: %" PRIu32 "\n",
+                  windowInfosDebug.pendingMessageCount);
+    result.append("\n");
 }
 
 mat4 SurfaceFlinger::calculateColorMatrix(float saturation) {
@@ -5595,29 +6192,11 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic error "-Wswitch-enum"
     switch (static_cast<ISurfaceComposerTag>(code)) {
-        case ENABLE_VSYNC_INJECTIONS:
-        case INJECT_VSYNC:
-            if (!hasMockHwc()) return PERMISSION_DENIED;
-            [[fallthrough]];
         // These methods should at minimum make sure that the client requested
         // access to SF.
-        case BOOT_FINISHED:
-        case CLEAR_ANIMATION_FRAME_STATS:
-        case GET_ANIMATION_FRAME_STATS:
-        case OVERRIDE_HDR_TYPES:
         case GET_HDR_CAPABILITIES:
-        case SET_DESIRED_DISPLAY_MODE_SPECS:
-        case GET_DESIRED_DISPLAY_MODE_SPECS:
-        case SET_ACTIVE_COLOR_MODE:
-        case SET_BOOT_DISPLAY_MODE:
         case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
         case GET_GAME_CONTENT_TYPE_SUPPORT:
-        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
-        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
-        case GET_DISPLAYED_CONTENT_SAMPLE:
-        case ADD_TUNNEL_MODE_ENABLED_LISTENER:
-        case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
-        case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
             // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary
             // permission dynamically. Don't use the permission cache for this check.
@@ -5630,100 +6209,38 @@
             }
             return OK;
         }
-        case GET_LAYER_DEBUG_INFO: {
-            IPCThreadState* ipc = IPCThreadState::self();
-            const int pid = ipc->getCallingPid();
-            const int uid = ipc->getCallingUid();
-            if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
-                ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
-                return PERMISSION_DENIED;
-            }
-            return OK;
-        }
-        // Used by apps to hook Choreographer to SurfaceFlinger.
-        case CREATE_DISPLAY_EVENT_CONNECTION:
         // The following calls are currently used by clients that do not
         // request necessary permissions. However, they do not expose any secret
         // information, so it is OK to pass them.
-        case AUTHENTICATE_SURFACE:
         case GET_ACTIVE_COLOR_MODE:
         case GET_ACTIVE_DISPLAY_MODE:
         case GET_DISPLAY_COLOR_MODES:
-        case GET_DISPLAY_NATIVE_PRIMARIES:
-        case GET_STATIC_DISPLAY_INFO:
-        case GET_DYNAMIC_DISPLAY_INFO:
         case GET_DISPLAY_MODES:
-        case GET_SUPPORTED_FRAME_TIMESTAMPS:
         // Calling setTransactionState is safe, because you need to have been
         // granted a reference to Client* and Handle* to do anything with it.
-        case SET_TRANSACTION_STATE:
-        case CREATE_CONNECTION:
-        case GET_COLOR_MANAGEMENT:
-        case GET_COMPOSITION_PREFERENCE:
-        case GET_PROTECTED_CONTENT_SUPPORT:
-        // setFrameRate() is deliberately available for apps to call without any
-        // special permissions.
-        case SET_FRAME_RATE:
-        case GET_DISPLAY_DECORATION_SUPPORT:
-        case SET_FRAME_TIMELINE_INFO:
-        case GET_GPU_CONTEXT_PRIORITY:
-        case GET_MAX_ACQUIRED_BUFFER_COUNT: {
+        case SET_TRANSACTION_STATE: {
             // This is not sensitive information, so should not require permission control.
             return OK;
         }
-        case ADD_FPS_LISTENER:
-        case REMOVE_FPS_LISTENER:
-        case ADD_REGION_SAMPLING_LISTENER:
-        case REMOVE_REGION_SAMPLING_LISTENER: {
-            // codes that require permission check
-            IPCThreadState* ipc = IPCThreadState::self();
-            const int pid = ipc->getCallingPid();
-            const int uid = ipc->getCallingUid();
-            if ((uid != AID_GRAPHICS) &&
-                !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
-                ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
-                return PERMISSION_DENIED;
-            }
-            return OK;
-        }
-        case ADD_TRANSACTION_TRACE_LISTENER: {
-            IPCThreadState* ipc = IPCThreadState::self();
-            const int uid = ipc->getCallingUid();
-            if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) {
-                return OK;
-            }
-            return PERMISSION_DENIED;
-        }
-        case SET_OVERRIDE_FRAME_RATE: {
-            const int uid = IPCThreadState::self()->getCallingUid();
-            if (uid == AID_ROOT || uid == AID_SYSTEM) {
-                return OK;
-            }
-            return PERMISSION_DENIED;
-        }
-        case ON_PULL_ATOM: {
-            const int uid = IPCThreadState::self()->getCallingUid();
-            if (uid == AID_SYSTEM) {
-                return OK;
-            }
-            return PERMISSION_DENIED;
-        }
-        case ADD_WINDOW_INFOS_LISTENER:
-        case REMOVE_WINDOW_INFOS_LISTENER: {
-            const int uid = IPCThreadState::self()->getCallingUid();
-            if (uid == AID_SYSTEM || uid == AID_GRAPHICS) {
-                return OK;
-            }
-            return PERMISSION_DENIED;
-        }
+        case BOOT_FINISHED:
+        // Used by apps to hook Choreographer to SurfaceFlinger.
+        case CREATE_DISPLAY_EVENT_CONNECTION:
+        case CREATE_CONNECTION:
         case CREATE_DISPLAY:
         case DESTROY_DISPLAY:
         case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
         case GET_PHYSICAL_DISPLAY_IDS:
         case GET_PHYSICAL_DISPLAY_TOKEN:
+        case AUTHENTICATE_SURFACE:
         case SET_POWER_MODE:
+        case GET_SUPPORTED_FRAME_TIMESTAMPS:
         case GET_DISPLAY_STATE:
         case GET_DISPLAY_STATS:
+        case GET_STATIC_DISPLAY_INFO:
+        case GET_DYNAMIC_DISPLAY_INFO:
+        case GET_DISPLAY_NATIVE_PRIMARIES:
+        case SET_ACTIVE_COLOR_MODE:
+        case SET_BOOT_DISPLAY_MODE:
         case CLEAR_BOOT_DISPLAY_MODE:
         case GET_BOOT_DISPLAY_MODE_SUPPORT:
         case SET_AUTO_LOW_LATENCY_MODE:
@@ -5731,12 +6248,43 @@
         case CAPTURE_LAYERS:
         case CAPTURE_DISPLAY:
         case CAPTURE_DISPLAY_BY_ID:
+        case CLEAR_ANIMATION_FRAME_STATS:
+        case GET_ANIMATION_FRAME_STATS:
+        case OVERRIDE_HDR_TYPES:
+        case ON_PULL_ATOM:
+        case ENABLE_VSYNC_INJECTIONS:
+        case INJECT_VSYNC:
+        case GET_LAYER_DEBUG_INFO:
+        case GET_COLOR_MANAGEMENT:
+        case GET_COMPOSITION_PREFERENCE:
+        case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
+        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
+        case GET_DISPLAYED_CONTENT_SAMPLE:
+        case GET_PROTECTED_CONTENT_SUPPORT:
         case IS_WIDE_COLOR_DISPLAY:
+        case ADD_REGION_SAMPLING_LISTENER:
+        case REMOVE_REGION_SAMPLING_LISTENER:
+        case ADD_FPS_LISTENER:
+        case REMOVE_FPS_LISTENER:
+        case ADD_TUNNEL_MODE_ENABLED_LISTENER:
+        case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
+        case ADD_WINDOW_INFOS_LISTENER:
+        case REMOVE_WINDOW_INFOS_LISTENER:
+        case SET_DESIRED_DISPLAY_MODE_SPECS:
+        case GET_DESIRED_DISPLAY_MODE_SPECS:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
         case SET_DISPLAY_BRIGHTNESS:
         case ADD_HDR_LAYER_INFO_LISTENER:
         case REMOVE_HDR_LAYER_INFO_LISTENER:
         case NOTIFY_POWER_BOOST:
+        case SET_GLOBAL_SHADOW_SETTINGS:
+        case GET_DISPLAY_DECORATION_SUPPORT:
+        case SET_FRAME_RATE:
+        case SET_OVERRIDE_FRAME_RATE:
+        case SET_FRAME_TIMELINE_INFO:
+        case ADD_TRANSACTION_TRACE_LISTENER:
+        case GET_GPU_CONTEXT_PRIORITY:
+        case GET_MAX_ACQUIRED_BUFFER_COUNT:
             LOG_FATAL("Deprecated opcode: %d, migrated to AIDL", code);
             return PERMISSION_DENIED;
     }
@@ -5818,15 +6366,8 @@
                 reply->writeInt32(0);
                 reply->writeInt32(mDebugDisableHWC);
                 return NO_ERROR;
-            case 1013: {
-                const auto display = getDefaultDisplayDevice();
-                if (!display) {
-                    return NAME_NOT_FOUND;
-                }
-
-                reply->writeInt32(display->getPageFlipCount());
-                return NO_ERROR;
-            }
+            case 1013: // Unused.
+                return NAME_NOT_FOUND;
             case 1014: {
                 Mutex::Autolock _l(mStateLock);
                 // daltonize
@@ -5897,17 +6438,8 @@
                 mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns);
                 return NO_ERROR;
             }
-            case 1020: { // Layer updates interceptor
-                n = data.readInt32();
-                if (n) {
-                    ALOGV("Interceptor enabled");
-                    mInterceptor->enable(mDrawingState.layersSortedByZ, mDrawingState.displays);
-                }
-                else{
-                    ALOGV("Interceptor disabled");
-                    mInterceptor->disable();
-                }
-                return NO_ERROR;
+            case 1020: { // Unused
+                return NAME_NOT_FOUND;
             }
             case 1021: { // Disable HWC virtual displays
                 const bool enable = data.readInt32() != 0;
@@ -5922,12 +6454,11 @@
                 updateColorMatrixLocked();
                 return NO_ERROR;
             }
-            case 1023: { // Set native mode
-                int32_t colorMode;
-
+            case 1023: { // Set color mode.
                 mDisplayColorSetting = static_cast<DisplayColorSetting>(data.readInt32());
-                if (data.readInt32(&colorMode) == NO_ERROR) {
-                    mForceColorMode = static_cast<ColorMode>(colorMode);
+
+                if (int32_t colorMode; data.readInt32(&colorMode) == NO_ERROR) {
+                    mForceColorMode = static_cast<ui::ColorMode>(colorMode);
                 }
                 scheduleRepaint();
                 return NO_ERROR;
@@ -5947,8 +6478,10 @@
                         int64_t startingTime =
                                 (fixedStartingTime) ? fixedStartingTime : systemTime();
                         mScheduler
-                                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) {
-                                    mLayerTracing.notify("start", startingTime);
+                                ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+                                                   kMainThreadContext) {
+                                    addToLayerTracing(true /* visibleRegionDirty */, startingTime,
+                                                      mLastCommittedVsyncId.value);
                                 })
                                 .wait();
                     }
@@ -6056,19 +6589,17 @@
                 return NO_ERROR;
             }
             case 1034: {
-                auto future = mScheduler->schedule([&] {
-                    switch (n = data.readInt32()) {
-                        case 0:
-                        case 1:
-                            FTL_FAKE_GUARD(mStateLock,
-                                           enableRefreshRateOverlay(static_cast<bool>(n)));
-                            break;
-                        default: {
-                            reply->writeBool(
-                                    FTL_FAKE_GUARD(mStateLock, isRefreshRateOverlayEnabled()));
-                        }
-                    }
-                });
+                auto future = mScheduler->schedule(
+                        [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
+                            switch (n = data.readInt32()) {
+                                case 0:
+                                case 1:
+                                    enableRefreshRateOverlay(static_cast<bool>(n));
+                                    break;
+                                default:
+                                    reply->writeBool(isRefreshRateOverlayEnabled());
+                            }
+                        });
 
                 future.wait();
                 return NO_ERROR;
@@ -6091,7 +6622,7 @@
                 }();
 
                 mDebugDisplayModeSetByBackdoor = false;
-                const status_t result = setActiveModeFromBackdoor(display, modeId);
+                const status_t result = setActiveModeFromBackdoor(display, DisplayModeId{modeId});
                 mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
                 return result;
             }
@@ -6101,7 +6632,7 @@
             case 1036: {
                 if (data.readInt32() > 0) { // turn on
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
 
@@ -6111,24 +6642,22 @@
                                 // defaultMode. The defaultMode doesn't matter for the override
                                 // policy though, since we set allowGroupSwitching to true, so it's
                                 // not a problem.
-                                scheduler::RefreshRateConfigs::Policy overridePolicy;
-                                overridePolicy.defaultMode = display->refreshRateConfigs()
+                                scheduler::RefreshRateSelector::OverridePolicy overridePolicy;
+                                overridePolicy.defaultMode = display->refreshRateSelector()
                                                                      .getDisplayManagerPolicy()
                                                                      .defaultMode;
                                 overridePolicy.allowGroupSwitching = true;
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(display, overridePolicy);
                             })
                             .get();
                 } else { // turn off
                     return mScheduler
-                            ->schedule([this] {
+                            ->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) {
                                 const auto display =
                                         FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
-                                constexpr bool kOverridePolicy = true;
-                                return setDesiredDisplayModeSpecsInternal(display, {},
-                                                                          kOverridePolicy);
+                                return setDesiredDisplayModeSpecsInternal(
+                                        display,
+                                        scheduler::RefreshRateSelector::NoOverridePolicy{});
                             })
                             .get();
                 }
@@ -6239,7 +6768,7 @@
     if (!updateOverlay) return;
 
     // Update the overlay on the main thread to avoid race conditions with
-    // mRefreshRateConfigs->getActiveMode()
+    // RefreshRateSelector::getActiveMode
     static_cast<void>(mScheduler->schedule([=] {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
         if (!display) {
@@ -6250,7 +6779,8 @@
 
         const auto desiredActiveMode = display->getDesiredActiveMode();
         const std::optional<DisplayModeId> desiredModeId = desiredActiveMode
-                ? std::make_optional(desiredActiveMode->mode->getId())
+                ? std::make_optional(desiredActiveMode->modeOpt->modePtr->getId())
+
                 : std::nullopt;
 
         const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -6303,7 +6833,7 @@
 }
 
 void SurfaceFlinger::toggleKernelIdleTimer() {
-    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+    using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
 
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display) {
@@ -6314,12 +6844,12 @@
     // If the support for kernel idle timer is disabled for the active display,
     // don't do anything.
     const std::optional<KernelIdleTimerController> kernelIdleTimerController =
-            display->refreshRateConfigs().kernelIdleTimerController();
+            display->refreshRateSelector().kernelIdleTimerController();
     if (!kernelIdleTimerController.has_value()) {
         return;
     }
 
-    const KernelIdleTimerAction action = display->refreshRateConfigs().getIdleTimerAction();
+    const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
 
     switch (action) {
         case KernelIdleTimerAction::TurnOff:
@@ -6335,7 +6865,7 @@
             if (!mKernelIdleTimerEnabled) {
                 ATRACE_INT("KernelIdleTimer", 1);
                 const std::chrono::milliseconds timeout =
-                        display->refreshRateConfigs().getIdleTimerTimeout();
+                        display->refreshRateSelector().getIdleTimerTimeout();
                 updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
                                       display->getPhysicalId());
                 mKernelIdleTimerEnabled = true;
@@ -6357,18 +6887,6 @@
     const int mApi;
 };
 
-static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-        case ColorMode::DISPLAY_BT2020:
-            return Dataspace::DISPLAY_P3;
-        default:
-            return Dataspace::V0_SRGB;
-    }
-}
-
 static bool hasCaptureBlackoutContentPermission() {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
@@ -6453,6 +6971,33 @@
     return NO_ERROR;
 }
 
+namespace {
+
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display,
+                                bool capturingHdrLayers, bool hintForSeamlessTransition) {
+    if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) {
+        return requestedDataspace;
+    }
+
+    const auto& state = display->getCompositionDisplay()->getState();
+
+    const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
+
+    // TODO: Enable once HDR screenshots are ready.
+    if constexpr (/* DISABLES CODE */ (false)) {
+        // For now since we only support 8-bit screenshots, just use HLG and
+        // assume that 1.0 >= display max luminance. This isn't quite as future
+        // proof as PQ is, but is good enough.
+        // Consider using PQ once we support 16-bit screenshots and we're able
+        // to consistently supply metadata to image encoders.
+        return ui::Dataspace::BT2020_HLG;
+    }
+
+    return dataspaceForColorMode;
+}
+
+} // namespace
+
 status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
                                         const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
@@ -6467,7 +7012,7 @@
     wp<const DisplayDevice> displayWeak;
     ui::LayerStack layerStack;
     ui::Size reqSize(args.width, args.height);
-    ui::Dataspace dataspace;
+    std::unordered_set<uint32_t> excludeLayerIds;
     {
         Mutex::Autolock lock(mStateLock);
         sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
@@ -6480,27 +7025,36 @@
             reqSize = display->getLayerStackSpaceRect().getSize();
         }
 
-        // The dataspace is depended on the color mode of display, that could use non-native mode
-        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
-        // and failed if display is not in native mode. This provide a way to force using native
-        // colors when capture.
-        dataspace = args.dataspace;
-        if (dataspace == ui::Dataspace::UNKNOWN) {
-            const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
-            dataspace = pickDataspaceFromColorMode(colorMode);
+        for (const auto& handle : args.excludeHandles) {
+            uint32_t excludeLayer = LayerHandle::getLayerId(handle);
+            if (excludeLayer != UNASSIGNED_LAYER_ID) {
+                excludeLayerIds.emplace(excludeLayer);
+            } else {
+                ALOGW("Invalid layer handle passed as excludeLayer to captureDisplay");
+                return NAME_NOT_FOUND;
+            }
         }
     }
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace,
-                                         args.useIdentityTransform, args.captureSecureLayers);
+        return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, args.dataspace,
+                                         args.useIdentityTransform, args.hintForSeamlessTransition,
+                                         args.captureSecureLayers);
     });
 
-    auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
-        traverseLayersInLayerStack(layerStack, args.uid, visitor);
-    };
+    GetLayerSnapshotsFunction getLayerSnapshots;
+    if (mLayerLifecycleManagerEnabled) {
+        getLayerSnapshots =
+                getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
+    } else {
+        auto traverseLayers = [this, args, excludeLayerIds,
+                               layerStack](const LayerVector::Visitor& visitor) {
+            traverseLayersInLayerStack(layerStack, args.uid, std::move(excludeLayerIds), visitor);
+        };
+        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    }
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
                                       args.pixelFormat, args.allowProtected, args.grayscale,
                                       captureListener);
     return fenceStatus(future.get());
@@ -6511,7 +7065,6 @@
     ui::LayerStack layerStack;
     wp<const DisplayDevice> displayWeak;
     ui::Size size;
-    ui::Dataspace dataspace;
     {
         Mutex::Autolock lock(mStateLock);
 
@@ -6523,20 +7076,25 @@
         displayWeak = display;
         layerStack = display->getLayerStack();
         size = display->getLayerStackSpaceRect().getSize();
-
-        dataspace =
-                pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
     }
 
     RenderAreaFuture renderAreaFuture = ftl::defer([=] {
-        return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace,
+        return DisplayRenderArea::create(displayWeak, Rect(), size, ui::Dataspace::UNKNOWN,
                                          false /* useIdentityTransform */,
+                                         false /* hintForSeamlessTransition */,
                                          false /* captureSecureLayers */);
     });
 
-    auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
-        traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
-    };
+    GetLayerSnapshotsFunction getLayerSnapshots;
+    if (mLayerLifecycleManagerEnabled) {
+        getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+                                                            /*snapshotFilterFn=*/nullptr);
+    } else {
+        auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
+            traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {}, visitor);
+        };
+        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    }
 
     if (captureListener == nullptr) {
         ALOGE("capture screen must provide a capture listener callback");
@@ -6546,7 +7104,7 @@
     constexpr bool kAllowProtected = false;
     constexpr bool kGrayscale = false;
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
                                       ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale,
                                       captureListener);
     return fenceStatus(future.get());
@@ -6564,8 +7122,8 @@
     ui::Size reqSize;
     sp<Layer> parent;
     Rect crop(args.sourceCrop);
-    std::unordered_set<sp<Layer>, SpHash<Layer>> excludeLayers;
-    ui::Dataspace dataspace;
+    std::unordered_set<uint32_t> excludeLayerIds;
+    ui::Dataspace dataspace = args.dataspace;
 
     // Call this before holding mStateLock to avoid any deadlocking.
     bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -6573,7 +7131,7 @@
     {
         Mutex::Autolock lock(mStateLock);
 
-        parent = fromHandle(args.layerHandle).promote();
+        parent = LayerHandle::getLayer(args.layerHandle);
         if (parent == nullptr) {
             ALOGE("captureLayers called with an invalid or removed parent");
             return NAME_NOT_FOUND;
@@ -6604,20 +7162,14 @@
         reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
 
         for (const auto& handle : args.excludeHandles) {
-            sp<Layer> excludeLayer = fromHandle(handle).promote();
-            if (excludeLayer != nullptr) {
-                excludeLayers.emplace(excludeLayer);
+            uint32_t excludeLayer = LayerHandle::getLayerId(handle);
+            if (excludeLayer != UNASSIGNED_LAYER_ID) {
+                excludeLayerIds.emplace(excludeLayer);
             } else {
                 ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
                 return NAME_NOT_FOUND;
             }
         }
-
-        // The dataspace is depended on the color mode of display, that could use non-native mode
-        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
-        // and failed if display is not in native mode. This provide a way to force using native
-        // colors when capture.
-        dataspace = args.dataspace;
     } // mStateLock
 
     // really small crop or frameScale
@@ -6626,49 +7178,78 @@
         return BAD_VALUE;
     }
 
-    Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
     bool childrenOnly = args.childrenOnly;
     RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
-        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
-                                                 childrenOnly, layerStackSpaceRect,
-                                                 args.captureSecureLayers);
-    });
-
-    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
-        parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-            if (!layer->isVisible()) {
-                return;
-            } else if (args.childrenOnly && layer == parent.get()) {
-                return;
-            } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
-                return;
+        ui::Transform layerTransform;
+        Rect layerBufferSize;
+        if (mLayerLifecycleManagerEnabled) {
+            frontend::LayerSnapshot* snapshot =
+                    mLayerSnapshotBuilder.getSnapshot(parent->getSequence());
+            if (!snapshot) {
+                ALOGW("Couldn't find layer snapshot for %d", parent->getSequence());
+            } else {
+                layerTransform = snapshot->localTransform;
+                layerBufferSize = snapshot->bufferSize;
             }
+        } else {
+            layerTransform = parent->getTransform();
+            layerBufferSize = parent->getBufferSize(parent->getDrawingState());
+        }
 
-            sp<Layer> p = layer;
-            while (p != nullptr) {
-                if (excludeLayers.count(p) != 0) {
+        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
+                                                 childrenOnly, args.captureSecureLayers,
+                                                 layerTransform, layerBufferSize,
+                                                 args.hintForSeamlessTransition);
+    });
+    GetLayerSnapshotsFunction getLayerSnapshots;
+    if (mLayerLifecycleManagerEnabled) {
+        std::optional<FloatRect> parentCrop = std::nullopt;
+        if (args.childrenOnly) {
+            parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
+                                        : crop.toFloatRect();
+        }
+
+        getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid,
+                                                            std::move(excludeLayerIds),
+                                                            args.childrenOnly, parentCrop);
+    } else {
+        auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
+            parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+                if (!layer->isVisible()) {
+                    return;
+                } else if (args.childrenOnly && layer == parent.get()) {
+                    return;
+                } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
                     return;
                 }
-                p = p->getParent();
-            }
 
-            visitor(layer);
-        });
-    };
+                auto p = sp<Layer>::fromExisting(layer);
+                while (p != nullptr) {
+                    if (excludeLayerIds.count(p->sequence) != 0) {
+                        return;
+                    }
+                    p = p->getParent();
+                }
+
+                visitor(layer);
+            });
+        };
+        getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+    }
 
     if (captureListener == nullptr) {
         ALOGE("capture screen must provide a capture listener callback");
         return BAD_VALUE;
     }
 
-    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+    auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
                                       args.pixelFormat, args.allowProtected, args.grayscale,
                                       captureListener);
     return fenceStatus(future.get());
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
-        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
         ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
         const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
@@ -6687,15 +7268,18 @@
     const bool supportsProtected = getRenderEngine().supportsProtectedContent();
     bool hasProtectedLayer = false;
     if (allowProtected && supportsProtected) {
-        auto future = mScheduler->schedule([=]() {
-            bool protectedLayerFound = false;
-            traverseLayers([&](Layer* layer) {
-                protectedLayerFound =
-                        protectedLayerFound || (layer->isVisible() && layer->isProtected());
-            });
-            return protectedLayerFound;
-        });
-        hasProtectedLayer = future.get();
+        hasProtectedLayer = mScheduler
+                                    ->schedule([=]() {
+                                        bool protectedLayerFound = false;
+                                        auto layers = getLayerSnapshots();
+                                        for (auto& [_, layerFe] : layers) {
+                                            protectedLayerFound |=
+                                                    (layerFe->mSnapshot->isVisible &&
+                                                     layerFe->mSnapshot->hasProtectedContent);
+                                        }
+                                        return protectedLayerFound;
+                                    })
+                                    .get();
     }
 
     const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6720,56 +7304,52 @@
             renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
                                                  renderengine::impl::ExternalTexture::Usage::
                                                          WRITEABLE);
-    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
+    return captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
                                false /* regionSampling */, grayscale, captureListener);
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
-        RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+        RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
         bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
     bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
 
-    auto future = mScheduler->schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable
-                                       -> ftl::SharedFuture<FenceResult> {
-        ScreenCaptureResults captureResults;
-        std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
-        if (!renderArea) {
-            ALOGW("Skipping screen capture because of invalid render area.");
-            if (captureListener) {
-                captureResults.result = NO_MEMORY;
-                captureListener->onScreenCaptureCompleted(captureResults);
-            }
-            return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
-        }
+    auto future = mScheduler->schedule(
+            [=, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
+                    kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+                ScreenCaptureResults captureResults;
+                std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
+                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();
+                }
 
-        ftl::SharedFuture<FenceResult> renderFuture;
-        renderArea->render([&] {
-            renderFuture =
-                    renderScreenImpl(*renderArea, traverseLayers, buffer, canCaptureBlackoutContent,
-                                     regionSampling, grayscale, captureResults);
-        });
+                ftl::SharedFuture<FenceResult> renderFuture;
+                renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+                    renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer,
+                                                    canCaptureBlackoutContent, regionSampling,
+                                                    grayscale, captureResults);
+                });
 
-        if (captureListener) {
-            // TODO: The future returned by std::async blocks the main thread. Return a chain of
-            // futures to the Binder thread instead.
-            (void)std::async([=]() mutable {
-                ATRACE_NAME("captureListener is nonnull!");
-                auto fenceResult = renderFuture.get();
-                // TODO(b/232535621): Change ScreenCaptureResults to store a FenceResult.
-                captureResults.result = fenceStatus(fenceResult);
-                captureResults.fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
-                captureListener->onScreenCaptureCompleted(captureResults);
+                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;
             });
-        }
-        return renderFuture;
-    });
-
-    if (captureListener) {
-        return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
-    }
 
     // Flatten nested futures.
     auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
@@ -6780,18 +7360,22 @@
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
+        std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer,
         bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
         ScreenCaptureResults& captureResults) {
     ATRACE_CALL();
 
-    traverseLayers([&](Layer* layer) {
-        captureResults.capturedSecureLayers =
-                captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
-    });
-
-    const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+    auto layers = getLayerSnapshots();
+    for (auto& [_, layerFE] : layers) {
+        frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
+        captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
+        captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
+        layerFE->mSnapshot->geomLayerTransform =
+                renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+        layerFE->mSnapshot->geomInverseLayerTransform =
+                layerFE->mSnapshot->geomLayerTransform.inverse();
+    }
 
     // We allow the system server to take screenshots of secure layers for
     // use in situations like the Screen-rotation animation and place
@@ -6801,146 +7385,153 @@
         return ftl::yield<FenceResult>(base::unexpected(PERMISSION_DENIED)).share();
     }
 
-    captureResults.buffer = buffer->getBuffer();
-    auto dataspace = renderArea.getReqDataSpace();
-    auto parent = renderArea.getParentLayer();
+    auto capturedBuffer = buffer;
+
+    auto requestedDataspace = renderArea->getReqDataSpace();
+    auto parent = renderArea->getParentLayer();
     auto renderIntent = RenderIntent::TONE_MAP_COLORIMETRIC;
     auto sdrWhitePointNits = DisplayDevice::sDefaultMaxLumiance;
     auto displayBrightnessNits = DisplayDevice::sDefaultMaxLumiance;
 
-    if ((dataspace == ui::Dataspace::UNKNOWN) && (parent != nullptr)) {
+    captureResults.capturedDataspace = requestedDataspace;
+
+    {
         Mutex::Autolock lock(mStateLock);
-        auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
-            return display.getLayerStack() == layerStack;
-        });
-        if (!display) {
-            // If the layer is not on a display, use the dataspace for the default display.
-            display = getDefaultDisplayDeviceLocked();
+        const DisplayDevice* display = nullptr;
+        if (parent) {
+            display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+                          return display.getLayerStack() == layerStack;
+                      }).get();
         }
 
-        const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
-        dataspace = pickDataspaceFromColorMode(colorMode);
-        renderIntent = display->getCompositionDisplay()->getState().renderIntent;
-        sdrWhitePointNits = display->getCompositionDisplay()->getState().sdrWhitePointNits;
-        displayBrightnessNits = display->getCompositionDisplay()->getState().displayBrightnessNits;
-    }
-    captureResults.capturedDataspace = dataspace;
+        if (display == nullptr) {
+            display = renderArea->getDisplayDevice().get();
+        }
 
-    const auto reqWidth = renderArea.getReqWidth();
-    const auto reqHeight = renderArea.getReqHeight();
-    const auto sourceCrop = renderArea.getSourceCrop();
-    const auto transform = renderArea.getTransform();
-    const auto rotation = renderArea.getRotationFlags();
-    const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect();
+        if (display == nullptr) {
+            display = getDefaultDisplayDeviceLocked().get();
+        }
 
-    renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
-
-    // assume that bounds are never offset, and that they are the same as the
-    // buffer bounds.
-    clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
-    clientCompositionDisplay.clip = sourceCrop;
-    clientCompositionDisplay.orientation = rotation;
-
-    clientCompositionDisplay.outputDataspace = dataspace;
-    clientCompositionDisplay.currentLuminanceNits = displayBrightnessNits;
-    clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
-    clientCompositionDisplay.renderIntent =
-            static_cast<aidl::android::hardware::graphics::composer3::RenderIntent>(renderIntent);
-
-    const float colorSaturation = grayscale ? 0 : 1;
-    clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation);
-
-    const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
-
-    compositionengine::LayerFE::LayerSettings fillLayer;
-    fillLayer.source.buffer.buffer = nullptr;
-    fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
-    fillLayer.geometry.boundaries =
-            FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
-    fillLayer.alpha = half(alpha);
-    clientCompositionLayers.push_back(fillLayer);
-
-    const auto display = renderArea.getDisplayDevice();
-    std::vector<Layer*> renderedLayers;
-    bool disableBlurs = false;
-    traverseLayers([&](Layer* layer) {
-        disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
-
-        Region clip(renderArea.getBounds());
-        compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
-                clip,
-                layer->needsFilteringForScreenshots(display.get(), transform) ||
-                        renderArea.needsFiltering(),
-                renderArea.isSecure(),
-                useProtected,
-                layerStackSpaceRect,
-                clientCompositionDisplay.outputDataspace,
-                true,  /* realContentIsVisible */
-                false, /* clearContent */
-                disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings::
-                                       BlurSetting::Disabled
-                             : compositionengine::LayerFE::ClientCompositionTargetSettings::
-                                       BlurSetting::Enabled,
-                isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits,
-
-        };
-        std::vector<compositionengine::LayerFE::LayerSettings> results =
-                layer->prepareClientCompositionList(targetSettings);
-        if (results.size() > 0) {
-            for (auto& settings : results) {
-                settings.geometry.positionTransform =
-                        transform.asMatrix4() * settings.geometry.positionTransform;
-                // There's no need to process blurs when we're executing region sampling,
-                // we're just trying to understand what we're drawing, and doing so without
-                // blurs is already a pretty good approximation.
-                if (regionSampling) {
-                    settings.backgroundBlurRadius = 0;
-                }
-                captureResults.capturedHdrLayers |= isHdrLayer(layer);
+        if (display != nullptr) {
+            const auto& state = display->getCompositionDisplay()->getState();
+            captureResults.capturedDataspace =
+                    pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
+                                      renderArea->getHintForSeamlessTransition());
+            sdrWhitePointNits = state.sdrWhitePointNits;
+            displayBrightnessNits = state.displayBrightnessNits;
+            if (sdrWhitePointNits > 1.0f) {
+                // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
+                // the SDR portion. 2.0 chosen by experimentation
+                constexpr float kMaxScreenshotHeadroom = 2.0f;
+                displayBrightnessNits =
+                        std::min(sdrWhitePointNits * kMaxScreenshotHeadroom, displayBrightnessNits);
             }
 
-            clientCompositionLayers.insert(clientCompositionLayers.end(),
-                                           std::make_move_iterator(results.begin()),
-                                           std::make_move_iterator(results.end()));
-            renderedLayers.push_back(layer);
+            if (requestedDataspace == ui::Dataspace::UNKNOWN) {
+                renderIntent = state.renderIntent;
+            }
         }
-
-    });
-
-    std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
-    clientRenderEngineLayers.reserve(clientCompositionLayers.size());
-    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
-                   std::back_inserter(clientRenderEngineLayers),
-                   [](compositionengine::LayerFE::LayerSettings& settings)
-                           -> renderengine::LayerSettings { return settings; });
-
-    // Use an empty fence for the buffer fence, since we just created the buffer so
-    // there is no need for synchronization with the GPU.
-    base::unique_fd bufferFence;
-    getRenderEngine().useProtectedContext(useProtected);
-
-    constexpr bool kUseFramebufferCache = false;
-    auto chain =
-            ftl::Future(getRenderEngine().drawLayers(clientCompositionDisplay,
-                                                     clientRenderEngineLayers, buffer,
-                                                     kUseFramebufferCache, std::move(bufferFence)))
-                    .then(&toFenceResult);
-
-    const auto future = chain.share();
-    for (auto* layer : renderedLayers) {
-        layer->onLayerDisplayed(future);
     }
 
-    // Always switch back to unprotected context.
-    getRenderEngine().useProtectedContext(false);
+    captureResults.buffer = capturedBuffer->getBuffer();
 
-    return future;
+    ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
+    if (!layers.empty()) {
+        const sp<LayerFE>& layerFE = layers.back().second;
+        layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
+    }
+
+    auto copyLayerFEs = [&layers]() {
+        std::vector<sp<compositionengine::LayerFE>> layerFEs;
+        layerFEs.reserve(layers.size());
+        for (const auto& [_, layerFE] : layers) {
+            layerFEs.push_back(layerFE);
+        }
+        return layerFEs;
+    };
+
+    auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
+                    sdrWhitePointNits, displayBrightnessNits, grayscale, layerFEs = copyLayerFEs(),
+                    layerStack, regionSampling, renderArea = std::move(renderArea),
+                    renderIntent]() -> FenceResult {
+        std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
+                mFactory.createCompositionEngine();
+        compositionEngine->setRenderEngine(mRenderEngine.get());
+
+        compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace,
+                                                             .renderIntent = renderIntent};
+
+        float targetBrightness = 1.0f;
+        if (dataspace == ui::Dataspace::BT2020_HLG) {
+            const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
+            // With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
+            // will appear way too bright.
+            if (maxBrightnessNits < 1000.f) {
+                targetBrightness = 1000.f / maxBrightnessNits;
+            }
+        }
+
+        std::shared_ptr<ScreenCaptureOutput> output = createScreenCaptureOutput(
+                ScreenCaptureOutputArgs{.compositionEngine = *compositionEngine,
+                                        .colorProfile = colorProfile,
+                                        .renderArea = *renderArea,
+                                        .layerStack = layerStack,
+                                        .buffer = std::move(buffer),
+                                        .sdrWhitePointNits = sdrWhitePointNits,
+                                        .displayBrightnessNits = displayBrightnessNits,
+                                        .targetBrightness = targetBrightness,
+                                        .regionSampling = regionSampling});
+
+        const float colorSaturation = grayscale ? 0 : 1;
+        compositionengine::CompositionRefreshArgs refreshArgs{
+                .outputs = {output},
+                .layers = std::move(layerFEs),
+                .updatingOutputGeometryThisFrame = true,
+                .updatingGeometryThisFrame = true,
+                .colorTransformMatrix = calculateColorMatrix(colorSaturation),
+        };
+        compositionEngine->present(refreshArgs);
+
+        return output->getRenderSurface()->getClientTargetAcquireFence();
+    };
+
+    // If RenderEngine is threaded, we can safely call CompositionEngine::present off the main
+    // thread as the RenderEngine::drawLayers call will run on RenderEngine's thread. Otherwise,
+    // we need RenderEngine to run on the main thread so we call CompositionEngine::present
+    // immediately.
+    //
+    // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
+    // to CompositionEngine::present.
+    const bool renderEngineIsThreaded = [&]() {
+        using Type = renderengine::RenderEngine::RenderEngineType;
+        const auto type = mRenderEngine->getRenderEngineType();
+        return type == Type::THREADED || type == Type::SKIA_GL_THREADED;
+    }();
+    auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share()
+                                                : ftl::yield(present()).share();
+
+    for (auto& [layer, layerFE] : layers) {
+        layer->onLayerDisplayed(ftl::Future(presentFuture)
+                                        .then([layerFE = std::move(layerFE)](FenceResult) {
+                                            return layerFE->stealCompositionResult()
+                                                    .releaseFences.back()
+                                                    .first.get();
+                                        })
+                                        .share(),
+                                ui::INVALID_LAYER_STACK);
+    }
+
+    return presentFuture;
 }
 
-void SurfaceFlinger::windowInfosReported() {
-    Mutex::Autolock _l(mStateLock);
-    signalSynchronousTransactions(CountDownLatch::eSyncInputWindows);
+void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const {
+    if (mLayerLifecycleManagerEnabled) {
+        for (auto& layer : mLegacyLayers) {
+            visitor(layer.second.get());
+        }
+    } else {
+        mDrawingState.traverse(visitor);
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -6958,6 +7549,7 @@
 }
 
 void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid,
+                                                std::unordered_set<uint32_t> excludeLayerIds,
                                                 const LayerVector::Visitor& visitor) {
     // We loop through the first level of layers without traversing,
     // as we need to determine which layers belong to the requested display.
@@ -6976,14 +7568,44 @@
             if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) {
                 return;
             }
+
+            if (!excludeLayerIds.empty()) {
+                auto p = sp<Layer>::fromExisting(layer);
+                while (p != nullptr) {
+                    if (excludeLayerIds.count(p->sequence) != 0) {
+                        return;
+                    }
+                    p = p->getParent();
+                }
+            }
+
             visitor(layer);
         });
     }
 }
 
+ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
+        PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
+    if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
+        schedulerMode.modePtr->getPhysicalDisplayId() == displayId) {
+        return schedulerMode;
+    }
+
+    return mPhysicalDisplays.get(displayId)
+            .transform(&PhysicalDisplay::snapshotRef)
+            .and_then([&](const display::DisplaySnapshot& snapshot) {
+                return snapshot.displayModes().get(defaultModeId);
+            })
+            .transform([](const DisplayModePtr& modePtr) {
+                return scheduler::FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+            });
+}
+
 status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
         const sp<DisplayDevice>& display,
-        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+        const scheduler::RefreshRateSelector::PolicyVariant& policy) {
+    const auto displayId = display->getPhysicalId();
+
     Mutex::Autolock lock(mStateLock);
 
     if (mDebugDisplayModeSetByBackdoor) {
@@ -6991,74 +7613,100 @@
         return NO_ERROR;
     }
 
-    const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy);
-    if (setPolicyResult < 0) {
-        return BAD_VALUE;
-    }
-    if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
-        return NO_ERROR;
+    auto& selector = display->refreshRateSelector();
+    using SetPolicyResult = scheduler::RefreshRateSelector::SetPolicyResult;
+
+    switch (selector.setPolicy(policy)) {
+        case SetPolicyResult::Invalid:
+            return BAD_VALUE;
+        case SetPolicyResult::Unchanged:
+            return NO_ERROR;
+        case SetPolicyResult::Changed:
+            break;
     }
 
-    if (display->isInternal() && !isDisplayActiveLocked(display)) {
+    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+                                           .transform(&PhysicalDisplay::isInternal)
+                                           .value_or(false);
+
+    if (isInternalDisplay && displayId != mActiveDisplayId) {
         // The policy will be be applied when the display becomes active.
-        ALOGV("%s(%s): Inactive display", __func__, to_string(display->getId()).c_str());
+        ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
         return NO_ERROR;
     }
 
-    return applyRefreshRateConfigsPolicy(display);
+    return applyRefreshRateSelectorPolicy(displayId, selector);
 }
 
-status_t SurfaceFlinger::applyRefreshRateConfigsPolicy(const sp<DisplayDevice>& display,
-                                                       bool force) {
-    const scheduler::RefreshRateConfigs::Policy currentPolicy =
-            display->refreshRateConfigs().getCurrentPolicy();
+status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
+        PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
+    const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
     ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
 
     // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
     // be depending in this callback.
-    const auto activeMode = display->getActiveMode();
-    if (isDisplayActiveLocked(display)) {
+    if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
         mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
         toggleKernelIdleTimer();
     } else {
         mScheduler->onNonPrimaryDisplayModeChanged(mAppConnectionHandle, activeMode);
     }
 
-    const DisplayModePtr preferredDisplayMode = [&] {
-        const auto schedulerMode = mScheduler->getPreferredDisplayMode();
-        if (schedulerMode && schedulerMode->getPhysicalDisplayId() == display->getPhysicalId()) {
-            return schedulerMode;
-        }
-
-        return display->getMode(currentPolicy.defaultMode);
-    }();
-
-    ALOGV("trying to switch to Scheduler preferred mode %d (%s)",
-          preferredDisplayMode->getId().value(), to_string(preferredDisplayMode->getFps()).c_str());
-
-    if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
-        ALOGV("switching to Scheduler preferred display mode %d",
-              preferredDisplayMode->getId().value());
-        setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed}, force);
-    } else {
-        LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
-                         preferredDisplayMode->getId().value());
+    auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
+    if (!preferredModeOpt) {
+        ALOGE("%s: Preferred mode is unknown", __func__);
+        return NAME_NOT_FOUND;
     }
 
+    auto preferredMode = std::move(*preferredModeOpt);
+    const auto preferredModeId = preferredMode.modePtr->getId();
+
+    ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
+          to_string(preferredMode.fps).c_str());
+
+    if (!selector.isModeAllowed(preferredMode)) {
+        ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
+        return INVALID_OPERATION;
+    }
+
+    setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
-        const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
-        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
-        float appRequestRefreshRateMax) {
+namespace {
+FpsRange translate(const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& aidlRange) {
+    return FpsRange{Fps::fromValue(aidlRange.min), Fps::fromValue(aidlRange.max)};
+}
+
+FpsRanges translate(const gui::DisplayModeSpecs::RefreshRateRanges& aidlRanges) {
+    return FpsRanges{translate(aidlRanges.physical), translate(aidlRanges.render)};
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange translate(const FpsRange& range) {
+    gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange aidlRange;
+    aidlRange.min = range.min.getValue();
+    aidlRange.max = range.max.getValue();
+    return aidlRange;
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) {
+    gui::DisplayModeSpecs::RefreshRateRanges aidlRanges;
+    aidlRanges.physical = translate(ranges.physical);
+    aidlRanges.render = translate(ranges.render);
+    return aidlRanges;
+}
+
+} // namespace
+
+status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                    const gui::DisplayModeSpecs& specs) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    auto future = mScheduler->schedule([=]() -> status_t {
+    auto future = mScheduler->schedule([=]() FTL_FAKE_GUARD(kMainThreadContext) -> status_t {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayToken));
         if (!display) {
             ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -7068,16 +7716,11 @@
             ALOGW("Attempt to set desired display modes for virtual display");
             return INVALID_OPERATION;
         } else {
-            using Policy = scheduler::RefreshRateConfigs::Policy;
-            const Policy policy{DisplayModeId(defaultMode),
-                                allowGroupSwitching,
-                                {Fps::fromValue(primaryRefreshRateMin),
-                                 Fps::fromValue(primaryRefreshRateMax)},
-                                {Fps::fromValue(appRequestRefreshRateMin),
-                                 Fps::fromValue(appRequestRefreshRateMax)}};
-            constexpr bool kOverridePolicy = false;
+            using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy;
+            const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges),
+                                translate(specs.appRequestRanges), specs.allowGroupSwitching};
 
-            return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
+            return setDesiredDisplayModeSpecsInternal(display, policy);
         }
     });
 
@@ -7085,16 +7728,10 @@
 }
 
 status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                                    ui::DisplayModeId* outDefaultMode,
-                                                    bool* outAllowGroupSwitching,
-                                                    float* outPrimaryRefreshRateMin,
-                                                    float* outPrimaryRefreshRateMax,
-                                                    float* outAppRequestRefreshRateMin,
-                                                    float* outAppRequestRefreshRateMax) {
+                                                    gui::DisplayModeSpecs* outSpecs) {
     ATRACE_CALL();
 
-    if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin ||
-        !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+    if (!displayToken || !outSpecs) {
         return BAD_VALUE;
     }
 
@@ -7108,21 +7745,15 @@
         return INVALID_OPERATION;
     }
 
-    scheduler::RefreshRateConfigs::Policy policy =
-            display->refreshRateConfigs().getDisplayManagerPolicy();
-    *outDefaultMode = policy.defaultMode.value();
-    *outAllowGroupSwitching = policy.allowGroupSwitching;
-    *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
-    *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
-    *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
-    *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
+    scheduler::RefreshRateSelector::Policy policy =
+            display->refreshRateSelector().getDisplayManagerPolicy();
+    outSpecs->defaultMode = policy.defaultMode.value();
+    outSpecs->allowGroupSwitching = policy.allowGroupSwitching;
+    outSpecs->primaryRanges = translate(policy.primaryRanges);
+    outSpecs->appRequestRanges = translate(policy.appRequestRanges);
     return NO_ERROR;
 }
 
-wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const {
-    return Layer::fromHandle(handle);
-}
-
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
     if (!layer->isRemovedFromCurrentState()) {
@@ -7185,45 +7816,12 @@
     // on the work to remove the table in that bug rather than adding more to
     // it.
     static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
-            {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
-            {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
+            {"org.chromium.arc.V1_0.TaskId", gui::METADATA_TASK_ID},
+            {"org.chromium.arc.V1_0.CursorInfo", gui::METADATA_MOUSE_CURSOR},
     };
     return genericLayerMetadataKeyMap;
 }
 
-status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                                      int8_t compatibility, int8_t changeFrameRateStrategy) {
-    if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
-                           "SurfaceFlinger::setFrameRate")) {
-        return BAD_VALUE;
-    }
-
-    static_cast<void>(mScheduler->schedule([=] {
-        Mutex::Autolock lock(mStateLock);
-        if (authenticateSurfaceTextureLocked(surface)) {
-            sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
-            if (layer == nullptr) {
-                ALOGE("Attempt to set frame rate on a layer that no longer exists");
-                return BAD_VALUE;
-            }
-            const auto strategy =
-                    Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
-            if (layer->setFrameRate(
-                        Layer::FrameRate(Fps::fromValue(frameRate),
-                                         Layer::FrameRate::convertCompatibility(compatibility),
-                                         strategy))) {
-                setTransactionFlags(eTraversalNeeded);
-            }
-        } else {
-            ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
-            return BAD_VALUE;
-        }
-        return NO_ERROR;
-    }));
-
-    return NO_ERROR;
-}
-
 status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) {
     PhysicalDisplayId displayId = [&]() {
         Mutex::Autolock lock(mStateLock);
@@ -7235,44 +7833,29 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
-                                              const FrameTimelineInfo& frameTimelineInfo) {
-    Mutex::Autolock lock(mStateLock);
-    if (!authenticateSurfaceTextureLocked(surface)) {
-        ALOGE("Attempt to set frame timeline info on an unrecognized IGraphicBufferProducer");
-        return BAD_VALUE;
-    }
-
-    sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
-    if (layer == nullptr) {
-        ALOGE("Attempt to set frame timeline info on a layer that no longer exists");
-        return BAD_VALUE;
-    }
-
-    layer->setFrameTimelineInfoForBuffer(frameTimelineInfo);
-    return NO_ERROR;
-}
-
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
-    for (const auto& [ignored, display] : mDisplays) {
-        if (display->isInternal()) {
-            display->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+    bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
+    for (const auto& [id, display] : mPhysicalDisplays) {
+        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+            if (setByHwc) {
+                const auto status =
+                        getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+                if (status != NO_ERROR) {
+                    ALOGE("Error updating the refresh rate changed callback debug enabled");
+                    return;
+                }
+            }
+
+            if (const auto device = getDisplayDeviceLocked(id)) {
+                device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
+                                                 mRefreshRateOverlayRenderRate,
+                                                 mRefreshRateOverlayShowInMiddle);
+            }
         }
     }
 }
 
-status_t SurfaceFlinger::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    if (!listener) {
-        return BAD_VALUE;
-    }
-
-    mInterceptor->addTransactionTraceListener(listener);
-
-    return NO_ERROR;
-}
-
-int SurfaceFlinger::getGPUContextPriority() {
+int SurfaceFlinger::getGpuContextPriority() {
     return getRenderEngine().getContextPriority();
 }
 
@@ -7290,7 +7873,7 @@
 
     if (!getHwComposer().isHeadless()) {
         if (const auto display = getDefaultDisplayDevice()) {
-            maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+            maxRefreshRate = display->refreshRateSelector().getSupportedRefreshRateRange().max;
         }
     }
 
@@ -7305,7 +7888,7 @@
         refreshRate = *frameRateOverride;
     } else if (!getHwComposer().isHeadless()) {
         if (const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked())) {
-            refreshRate = display->refreshRateConfigs().getActiveMode()->getFps();
+            refreshRate = display->refreshRateSelector().getActiveMode().fps;
         }
     }
 
@@ -7318,7 +7901,7 @@
     return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
 }
 
-void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state) {
+void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, VsyncId vsyncId) {
     sp<Layer> layer = state.layer.promote();
     if (!layer) {
         ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get());
@@ -7348,9 +7931,19 @@
         parent->addChild(layer);
     }
 
-    layer->updateTransformHint(mActiveDisplayTransformHint);
+    ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current);
+    sp<const DisplayDevice> hintDisplay;
+    // Find the display that includes the layer.
+    for (const auto& [token, display] : mDisplays) {
+        if (display->getLayerStack() == layerStack) {
+            hintDisplay = display;
+            break;
+        }
+    }
 
-    mInterceptor->saveSurfaceCreation(layer);
+    if (hintDisplay) {
+        layer->updateTransformHint(hintDisplay->getTransformHint());
+    }
 }
 
 void SurfaceFlinger::sample() {
@@ -7361,49 +7954,48 @@
     mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
 }
 
-void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
-    mScheduler->onActiveDisplayAreaChanged(activeDisplay->getWidth() * activeDisplay->getHeight());
-    getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
+void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
+    mScheduler->onActiveDisplayAreaChanged(activeDisplay.getWidth() * activeDisplay.getHeight());
+    getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
 }
 
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
+                                                  const DisplayDevice& activeDisplay) {
     ATRACE_CALL();
 
-    // During boot, SF powers on the primary display, which is the first display to be active. In
-    // that case, there is no need to force setDesiredActiveMode, because DM is about to send its
-    // policy via setDesiredDisplayModeSpecs.
+    // For the first display activated during boot, there is no need to force setDesiredActiveMode,
+    // because DM is about to send its policy via setDesiredDisplayModeSpecs.
     bool forceApplyPolicy = false;
 
-    if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
-        display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+    if (inactiveDisplayPtr) {
+        inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
         forceApplyPolicy = true;
     }
 
-    if (!activeDisplay) {
-        ALOGE("%s: activeDisplay is null", __func__);
-        return;
-    }
+    mActiveDisplayId = activeDisplay.getPhysicalId();
+    activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
 
-    ALOGI("Active display is %s", to_string(activeDisplay->getPhysicalId()).c_str());
+    resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
 
-    mActiveDisplayToken = activeDisplay->getDisplayToken();
-    activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
-    updateInternalDisplayVsyncLocked(activeDisplay);
     mScheduler->setModeChangePending(false);
-    mScheduler->setRefreshRateConfigs(activeDisplay->holdRefreshRateConfigs());
-    onActiveDisplaySizeChanged(activeDisplay);
-    mActiveDisplayTransformHint = activeDisplay->getTransformHint();
+    mScheduler->setPacesetterDisplay(mActiveDisplayId);
 
-    // The policy of the new active/leader display may have changed while it was inactive. In that
-    // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
-    // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
-    // and the kernel idle timer of the newly active display must be toggled.
-    applyRefreshRateConfigsPolicy(activeDisplay, forceApplyPolicy);
+    onActiveDisplaySizeChanged(activeDisplay);
+    mActiveDisplayTransformHint = activeDisplay.getTransformHint();
+    sActiveDisplayRotationFlags = ui::Transform::toRotationFlags(activeDisplay.getOrientation());
+
+    // The policy of the new active/pacesetter display may have changed while it was inactive. In
+    // that case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In
+    // either case, the Scheduler's cachedModeChangedParams must be initialized to the newly active
+    // mode, and the kernel idle timer of the newly active display must be toggled.
+    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(),
+                                   forceApplyPolicy);
 }
 
 status_t SurfaceFlinger::addWindowInfosListener(
-        const sp<IWindowInfosListener>& windowInfosListener) const {
+        const sp<IWindowInfosListener>& windowInfosListener) {
     mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
+    setTransactionFlags(eInputInfoUpdateNeeded);
     return NO_ERROR;
 }
 
@@ -7414,76 +8006,424 @@
 }
 
 std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
-        const BufferData& bufferData, const char* layerName) const {
-    bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
-    bool bufferSizeExceedsLimit = false;
-    std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
-    if (cacheIdChanged && bufferData.buffer != nullptr) {
-        bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
-                                                            bufferData.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
-            buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+        BufferData& bufferData, const char* layerName, uint64_t transactionId) {
+    if (bufferData.buffer &&
+        exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(), bufferData.buffer->getHeight())) {
+        std::string errorMessage =
+                base::StringPrintf("Attempted to create an ExternalTexture with size (%u, %u) for "
+                                   "layer %s that exceeds render target size limit of %u.",
+                                   bufferData.buffer->getWidth(), bufferData.buffer->getHeight(),
+                                   layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
+        ALOGD("%s", errorMessage.c_str());
+        if (bufferData.releaseBufferListener) {
+            bufferData.releaseBufferListener->onTransactionQueueStalled(
+                    String8(errorMessage.c_str()));
         }
-    } else if (cacheIdChanged) {
-        buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
-    } else if (bufferData.buffer != nullptr) {
-        bufferSizeExceedsLimit = exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
-                                                            bufferData.buffer->getHeight());
-        if (!bufferSizeExceedsLimit) {
-            buffer = std::make_shared<
-                    renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
-                                                         renderengine::impl::ExternalTexture::
-                                                                 Usage::READABLE);
-        }
+        return nullptr;
     }
-    ALOGE_IF(bufferSizeExceedsLimit,
-             "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
-             "limit.",
-             layerName);
-    return buffer;
+
+    bool cachedBufferChanged =
+            bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+    if (cachedBufferChanged && bufferData.buffer) {
+        auto result = ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+        if (result.ok()) {
+            return result.value();
+        }
+
+        if (result.error() == ClientCache::AddError::CacheFull) {
+            ALOGE("Attempted to create an ExternalTexture for layer %s but CacheFull", layerName);
+
+            if (bufferData.releaseBufferListener) {
+                bufferData.releaseBufferListener->onTransactionQueueStalled(
+                        String8("Buffer processing hung due to full buffer cache"));
+            }
+        }
+
+        return nullptr;
+    }
+
+    if (cachedBufferChanged) {
+        return ClientCache::getInstance().get(bufferData.cachedBuffer);
+    }
+
+    if (bufferData.buffer) {
+        return std::make_shared<
+                renderengine::impl::ExternalTexture>(bufferData.buffer, getRenderEngine(),
+                                                     renderengine::impl::ExternalTexture::Usage::
+                                                             READABLE);
+    }
+
+    return nullptr;
 }
 
-bool SurfaceFlinger::commitCreatedLayers() {
-    std::vector<LayerCreatedState> createdLayers;
+bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) {
+    std::vector<MirrorDisplayState> mirrorDisplays;
     {
-        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
-        createdLayers = std::move(mCreatedLayers);
-        mCreatedLayers.clear();
-        if (createdLayers.size() == 0) {
+        std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
+        mirrorDisplays = std::move(mMirrorDisplays);
+        mMirrorDisplays.clear();
+        if (mirrorDisplays.size() == 0) {
             return false;
         }
     }
 
+    sp<IBinder> unused;
+    for (const auto& mirrorDisplay : mirrorDisplays) {
+        // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
+        // accidentally.
+        sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
+        rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
+        for (const auto& layer : mDrawingState.layersSortedByZ) {
+            if (layer->getLayerStack() != mirrorDisplay.layerStack ||
+                layer->isInternalDisplayOverlay()) {
+                continue;
+            }
+
+            LayerCreationArgs mirrorArgs(this, mirrorDisplay.client, "MirrorLayerParent",
+                                         ISurfaceComposerClient::eNoColorFill,
+                                         gui::LayerMetadata());
+            sp<Layer> childMirror;
+            {
+                Mutex::Autolock lock(mStateLock);
+                createEffectLayer(mirrorArgs, &unused, &childMirror);
+                MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
+                childMirror->setClonedChild(layer->createClone(childMirror->getSequence()));
+                childMirror->reparent(mirrorDisplay.rootHandle);
+            }
+            // lock on mStateLock needs to be released before binder handle gets destroyed
+            unused.clear();
+        }
+    }
+    return true;
+}
+
+bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId,
+                                         std::vector<LayerCreatedState>& createdLayers) {
+    if (createdLayers.size() == 0) {
+        return false;
+    }
+
     Mutex::Autolock _l(mStateLock);
     for (const auto& createdLayer : createdLayers) {
-        handleLayerCreatedLocked(createdLayer);
+        handleLayerCreatedLocked(createdLayer, vsyncId);
     }
-    createdLayers.clear();
     mLayersAdded = true;
-    return true;
+    return mLayersAdded;
+}
+
+void SurfaceFlinger::updateLayerMetadataSnapshot() {
+    LayerMetadata parentMetadata;
+    for (const auto& layer : mDrawingState.layersSortedByZ) {
+        layer->updateMetadataSnapshot(parentMetadata);
+    }
+
+    std::unordered_set<Layer*> visited;
+    mDrawingState.traverse([&visited](Layer* layer) {
+        if (visited.find(layer) != visited.end()) {
+            return;
+        }
+
+        // If the layer isRelativeOf, then either it's relative metadata will be set
+        // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
+        // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
+        // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
+        if (layer->getDrawingState().isRelativeOf) {
+            layer->editLayerSnapshot()->relativeLayerMetadata = {};
+            return;
+        }
+
+        layer->updateRelativeMetadataSnapshot({}, visited);
+    });
+}
+
+void SurfaceFlinger::moveSnapshotsFromCompositionArgs(
+        compositionengine::CompositionRefreshArgs& refreshArgs,
+        std::vector<std::pair<Layer*, LayerFE*>>& layers) {
+    if (mLayerLifecycleManagerEnabled) {
+        std::vector<std::unique_ptr<frontend::LayerSnapshot>>& snapshots =
+                mLayerSnapshotBuilder.getSnapshots();
+        for (auto [_, layerFE] : layers) {
+            auto i = layerFE->mSnapshot->globalZ;
+            snapshots[i] = std::move(layerFE->mSnapshot);
+        }
+    }
+    if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+        for (auto [layer, layerFE] : layers) {
+            layer->updateLayerSnapshot(std::move(layerFE->mSnapshot));
+        }
+    }
+}
+
+std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToCompositionArgs(
+        compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) {
+    std::vector<std::pair<Layer*, LayerFE*>> layers;
+    if (mLayerLifecycleManagerEnabled) {
+        nsecs_t currentTime = systemTime();
+        mLayerSnapshotBuilder.forEachVisibleSnapshot(
+                [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+                    if (cursorOnly &&
+                        snapshot->compositionType !=
+                                aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
+                        return;
+                    }
+
+                    if (!snapshot->hasSomethingToDraw()) {
+                        return;
+                    }
+
+                    auto it = mLegacyLayers.find(snapshot->sequence);
+                    LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+                                        "Couldnt find layer object for %s",
+                                        snapshot->getDebugString().c_str());
+                    auto& legacyLayer = it->second;
+                    sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path);
+                    snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence);
+                    layerFE->mSnapshot = std::move(snapshot);
+                    refreshArgs.layers.push_back(layerFE);
+                    layers.emplace_back(legacyLayer.get(), layerFE.get());
+                });
+    }
+    if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+        auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) {
+            if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
+                if (cursorOnly &&
+                    layer->getLayerSnapshot()->compositionType !=
+                            aidl::android::hardware::graphics::composer3::Composition::CURSOR)
+                    return;
+                layer->updateSnapshot(refreshArgs.updatingGeometryThisFrame);
+                layerFE->mSnapshot = layer->stealLayerSnapshot();
+                refreshArgs.layers.push_back(layerFE);
+                layers.emplace_back(layer, layerFE.get());
+            }
+        };
+
+        if (cursorOnly || !mVisibleRegionsDirty) {
+            // for hot path avoid traversals by walking though the previous composition list
+            for (sp<Layer> layer : mPreviouslyComposedLayers) {
+                moveSnapshots(layer.get());
+            }
+        } else {
+            mPreviouslyComposedLayers.clear();
+            mDrawingState.traverseInZOrder(
+                    [&moveSnapshots](Layer* layer) { moveSnapshots(layer); });
+            mPreviouslyComposedLayers.reserve(layers.size());
+            for (auto [layer, _] : layers) {
+                mPreviouslyComposedLayers.push_back(sp<Layer>::fromExisting(layer));
+            }
+        }
+    }
+
+    return layers;
+}
+
+std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
+SurfaceFlinger::getLayerSnapshotsForScreenshots(
+        std::optional<ui::LayerStack> layerStack, uint32_t uid,
+        std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+                snapshotFilterFn) {
+    return [&, layerStack, uid]() {
+        std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+        bool stopTraversal = false;
+        mLayerSnapshotBuilder.forEachVisibleSnapshot(
+                [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+                    if (stopTraversal) {
+                        return;
+                    }
+                    if (layerStack && snapshot->outputFilter.layerStack != *layerStack) {
+                        return;
+                    }
+                    if (uid != CaptureArgs::UNSET_UID && snapshot->uid != uid) {
+                        return;
+                    }
+                    if (!snapshot->hasSomethingToDraw()) {
+                        return;
+                    }
+                    if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) {
+                        return;
+                    }
+
+                    auto it = mLegacyLayers.find(snapshot->sequence);
+                    LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(),
+                                        "Couldnt find layer object for %s",
+                                        snapshot->getDebugString().c_str());
+                    Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
+                    sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
+                    layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
+                    layers.emplace_back(legacyLayer, std::move(layerFE));
+                });
+
+        return layers;
+    };
+}
+
+std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
+SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
+                                                uint32_t uid,
+                                                std::unordered_set<uint32_t> excludeLayerIds) {
+    return [&, layerStack, uid, excludeLayerIds = std::move(excludeLayerIds)]() {
+        if (excludeLayerIds.empty()) {
+            auto getLayerSnapshotsFn =
+                    getLayerSnapshotsForScreenshots(layerStack, uid, /*snapshotFilterFn=*/nullptr);
+            std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
+            return layers;
+        }
+
+        frontend::LayerSnapshotBuilder::Args
+                args{.root = mLayerHierarchyBuilder.getHierarchy(),
+                     .layerLifecycleManager = mLayerLifecycleManager,
+                     .forceUpdate = frontend::LayerSnapshotBuilder::ForceUpdateFlags::HIERARCHY,
+                     .displays = mFrontEndDisplayInfos,
+                     .displayChanges = true,
+                     .globalShadowSettings = mDrawingState.globalShadowSettings,
+                     .supportsBlur = mSupportsBlur,
+                     .forceFullDamage = mForceFullDamage,
+                     .excludeLayerIds = std::move(excludeLayerIds),
+                     .supportedLayerGenericMetadata =
+                             getHwComposer().getSupportedLayerGenericMetadata(),
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+        mLayerSnapshotBuilder.update(args);
+
+        auto getLayerSnapshotsFn =
+                getLayerSnapshotsForScreenshots(layerStack, uid, /*snapshotFilterFn=*/nullptr);
+        std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
+
+        args.excludeLayerIds.clear();
+        mLayerSnapshotBuilder.update(args);
+
+        return layers;
+    };
+}
+
+std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>
+SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid,
+                                                std::unordered_set<uint32_t> excludeLayerIds,
+                                                bool childrenOnly,
+                                                const std::optional<FloatRect>& parentCrop) {
+    return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
+            parentCrop]() {
+        auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
+        frontend::LayerSnapshotBuilder::Args
+                args{.root = root,
+                     .layerLifecycleManager = mLayerLifecycleManager,
+                     .forceUpdate = frontend::LayerSnapshotBuilder::ForceUpdateFlags::HIERARCHY,
+                     .displays = mFrontEndDisplayInfos,
+                     .displayChanges = true,
+                     .globalShadowSettings = mDrawingState.globalShadowSettings,
+                     .supportsBlur = mSupportsBlur,
+                     .forceFullDamage = mForceFullDamage,
+                     .parentCrop = parentCrop,
+                     .excludeLayerIds = std::move(excludeLayerIds),
+                     .supportedLayerGenericMetadata =
+                             getHwComposer().getSupportedLayerGenericMetadata(),
+                     .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()};
+        mLayerSnapshotBuilder.update(args);
+
+        auto getLayerSnapshotsFn =
+                getLayerSnapshotsForScreenshots({}, uid, /*snapshotFilterFn=*/nullptr);
+        std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn();
+        args.root = mLayerHierarchyBuilder.getHierarchy();
+        args.parentCrop.reset();
+        args.excludeLayerIds.clear();
+        mLayerSnapshotBuilder.update(args);
+        return layers;
+    };
+}
+
+frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
+    frontend::Update update;
+    ATRACE_NAME("TransactionHandler:flushTransactions");
+    // Locking:
+    // 1. to prevent onHandleDestroyed from being called while the state lock is held,
+    // we must keep a copy of the transactions (specifically the composer
+    // states) around outside the scope of the lock.
+    // 2. Transactions and created layers do not share a lock. To prevent applying
+    // transactions with layers still in the createdLayer queue, flush the transactions
+    // before committing the created layers.
+    update.transactions = mTransactionHandler.flushTransactions();
+    {
+        // TODO(b/238781169) lockless queue this and keep order.
+        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
+        update.layerCreatedStates = std::move(mCreatedLayers);
+        mCreatedLayers.clear();
+        update.newLayers = std::move(mNewLayers);
+        mNewLayers.clear();
+        update.layerCreationArgs = std::move(mNewLayerArgs);
+        mNewLayerArgs.clear();
+        update.destroyedHandles = std::move(mDestroyedHandles);
+        mDestroyedHandles.clear();
+    }
+    return update;
+}
+
+void SurfaceFlinger::addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId) {
+    const uint32_t tracingFlags = mLayerTracing.getFlags();
+    LayersProto layers(dumpDrawingStateProto(tracingFlags));
+    if (tracingFlags & LayerTracing::TRACE_EXTRA) {
+        dumpOffscreenLayersProto(layers);
+    }
+    std::string hwcDump;
+    if (tracingFlags & LayerTracing::TRACE_HWC) {
+        dumpHwc(hwcDump);
+    }
+    auto displays = dumpDisplayProto();
+    mLayerTracing.notify(visibleRegionDirty, time, vsyncId, &layers, std::move(hwcDump), &displays);
 }
 
 // gui::ISurfaceComposer
 
-binder::Status SurfaceComposerAIDL::createDisplay(const std::string& displayName, bool secure,
-                                                  sp<IBinder>* outDisplay) {
+binder::Status SurfaceComposerAIDL::bootFinished() {
     status_t status = checkAccessPermission();
-    if (status == OK) {
-        String8 displayName8 = String8::format("%s", displayName.c_str());
-        *outDisplay = mFlinger->createDisplay(displayName8, secure);
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+    mFlinger->bootFinished();
+    return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::createDisplayEventConnection(
+        VsyncSource vsyncSource, EventRegistration eventRegistration,
+        const sp<IBinder>& layerHandle, sp<IDisplayEventConnection>* outConnection) {
+    sp<IDisplayEventConnection> conn =
+            mFlinger->createDisplayEventConnection(vsyncSource, eventRegistration, layerHandle);
+    if (conn == nullptr) {
+        *outConnection = nullptr;
+        return binderStatusFromStatusT(BAD_VALUE);
+    } else {
+        *outConnection = conn;
         return binder::Status::ok();
     }
-    return binder::Status::fromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::createConnection(sp<gui::ISurfaceComposerClient>* outClient) {
+    const sp<Client> client = sp<Client>::make(mFlinger);
+    if (client->initCheck() == NO_ERROR) {
+        *outClient = client;
+        return binder::Status::ok();
+    } else {
+        *outClient = nullptr;
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+}
+
+binder::Status SurfaceComposerAIDL::createDisplay(const std::string& displayName, bool secure,
+                                                  float requestedRefreshRate,
+                                                  sp<IBinder>* outDisplay) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+    String8 displayName8 = String8::format("%s", displayName.c_str());
+    *outDisplay = mFlinger->createDisplay(displayName8, secure, requestedRefreshRate);
+    return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::destroyDisplay(const sp<IBinder>& display) {
     status_t status = checkAccessPermission();
-    if (status == OK) {
-        mFlinger->destroyDisplay(display);
-        return binder::Status::ok();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
     }
-    return binder::Status::fromStatusT(status);
+    mFlinger->destroyDisplay(display);
+    return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) {
@@ -7497,22 +8437,12 @@
     return binder::Status::ok();
 }
 
-binder::Status SurfaceComposerAIDL::getPrimaryPhysicalDisplayId(int64_t* outDisplayId) {
-    status_t status = checkAccessPermission();
-    if (status != OK) {
-        return binder::Status::fromStatusT(status);
-    }
-
-    PhysicalDisplayId id;
-    status = mFlinger->getPrimaryPhysicalDisplayId(&id);
-    if (status == NO_ERROR) {
-        *outDisplayId = id.value;
-    }
-    return binder::Status::fromStatusT(status);
-}
-
 binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
                                                             sp<IBinder>* outDisplay) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
     *outDisplay = mFlinger->getPhysicalDisplayToken(*id);
     return binder::Status::ok();
@@ -7520,12 +8450,25 @@
 
 binder::Status SurfaceComposerAIDL::setPowerMode(const sp<IBinder>& display, int mode) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->setPowerMode(display, mode);
     return binder::Status::ok();
 }
 
+binder::Status SurfaceComposerAIDL::getSupportedFrameTimestamps(
+        std::vector<FrameEvent>* outSupported) {
+    status_t status;
+    if (!outSupported) {
+        status = UNEXPECTED_NULL;
+    } else {
+        outSupported->clear();
+        status = mFlinger->getSupportedFrameTimestamps(outSupported);
+    }
+    return binderStatusFromStatusT(status);
+}
+
 binder::Status SurfaceComposerAIDL::getDisplayStats(const sp<IBinder>& display,
                                                     gui::DisplayStatInfo* outStatInfo) {
     DisplayStatInfo statInfo;
@@ -7534,7 +8477,7 @@
         outStatInfo->vsyncTime = static_cast<long>(statInfo.vsyncTime);
         outStatInfo->vsyncPeriod = static_cast<long>(statInfo.vsyncPeriod);
     }
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::getDisplayState(const sp<IBinder>& display,
@@ -7547,37 +8490,229 @@
         outState->layerStackSpaceRect.width = state.layerStackSpaceRect.width;
         outState->layerStackSpaceRect.height = state.layerStackSpaceRect.height;
     }
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
+                                                         gui::StaticDisplayInfo* outInfo) {
+    using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
+    ui::StaticDisplayInfo info;
+
+    status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
+    if (status == NO_ERROR) {
+        // convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
+        outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
+        outInfo->density = info.density;
+        outInfo->secure = info.secure;
+        outInfo->installOrientation = static_cast<gui::Rotation>(info.installOrientation);
+
+        gui::DeviceProductInfo dinfo;
+        std::optional<DeviceProductInfo> dpi = info.deviceProductInfo;
+        dinfo.name = std::move(dpi->name);
+        dinfo.manufacturerPnpId =
+                std::vector<uint8_t>(dpi->manufacturerPnpId.begin(), dpi->manufacturerPnpId.end());
+        dinfo.productId = dpi->productId;
+        dinfo.relativeAddress =
+                std::vector<uint8_t>(dpi->relativeAddress.begin(), dpi->relativeAddress.end());
+        if (const auto* model =
+                    std::get_if<DeviceProductInfo::ModelYear>(&dpi->manufactureOrModelDate)) {
+            gui::DeviceProductInfo::ModelYear modelYear;
+            modelYear.year = model->year;
+            dinfo.manufactureOrModelDate.set<Tag::modelYear>(modelYear);
+        } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureYear>(
+                           &dpi->manufactureOrModelDate)) {
+            gui::DeviceProductInfo::ManufactureYear date;
+            date.modelYear.year = manufacture->year;
+            dinfo.manufactureOrModelDate.set<Tag::manufactureYear>(date);
+        } else if (const auto* manufacture = std::get_if<DeviceProductInfo::ManufactureWeekAndYear>(
+                           &dpi->manufactureOrModelDate)) {
+            gui::DeviceProductInfo::ManufactureWeekAndYear date;
+            date.manufactureYear.modelYear.year = manufacture->year;
+            date.week = manufacture->week;
+            dinfo.manufactureOrModelDate.set<Tag::manufactureWeekAndYear>(date);
+        }
+
+        outInfo->deviceProductInfo = dinfo;
+    }
+    return binderStatusFromStatusT(status);
+}
+
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+                                                        gui::DynamicDisplayInfo*& outInfo) {
+    // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+    outInfo->supportedDisplayModes.clear();
+    outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+    for (const auto& mode : info.supportedDisplayModes) {
+        gui::DisplayMode outMode;
+        outMode.id = mode.id;
+        outMode.resolution.width = mode.resolution.width;
+        outMode.resolution.height = mode.resolution.height;
+        outMode.xDpi = mode.xDpi;
+        outMode.yDpi = mode.yDpi;
+        outMode.refreshRate = mode.refreshRate;
+        outMode.appVsyncOffset = mode.appVsyncOffset;
+        outMode.sfVsyncOffset = mode.sfVsyncOffset;
+        outMode.presentationDeadline = mode.presentationDeadline;
+        outMode.group = mode.group;
+        std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+                       std::back_inserter(outMode.supportedHdrTypes),
+                       [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+        outInfo->supportedDisplayModes.push_back(outMode);
+    }
+
+    outInfo->activeDisplayModeId = info.activeDisplayModeId;
+    outInfo->renderFrameRate = info.renderFrameRate;
+
+    outInfo->supportedColorModes.clear();
+    outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+    for (const auto& cmode : info.supportedColorModes) {
+        outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+    }
+
+    outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+    gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+    hdrCapabilities.supportedHdrTypes.clear();
+    hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+    for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+        hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+    }
+    hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+    hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+    hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+    outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+    outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+    outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+        const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
+    ui::DynamicDisplayInfo info;
+    status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
+    if (status == NO_ERROR) {
+        getDynamicDisplayInfoInternal(info, outInfo);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+                                                                gui::DynamicDisplayInfo* outInfo) {
+    ui::DynamicDisplayInfo info;
+    status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+    if (status == NO_ERROR) {
+        getDynamicDisplayInfoInternal(info, outInfo);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDisplayNativePrimaries(const sp<IBinder>& display,
+                                                              gui::DisplayPrimaries* outPrimaries) {
+    ui::DisplayPrimaries primaries;
+    status_t status = mFlinger->getDisplayNativePrimaries(display, primaries);
+    if (status == NO_ERROR) {
+        outPrimaries->red.X = primaries.red.X;
+        outPrimaries->red.Y = primaries.red.Y;
+        outPrimaries->red.Z = primaries.red.Z;
+
+        outPrimaries->green.X = primaries.green.X;
+        outPrimaries->green.Y = primaries.green.Y;
+        outPrimaries->green.Z = primaries.green.Z;
+
+        outPrimaries->blue.X = primaries.blue.X;
+        outPrimaries->blue.Y = primaries.blue.Y;
+        outPrimaries->blue.Z = primaries.blue.Z;
+
+        outPrimaries->white.X = primaries.white.X;
+        outPrimaries->white.Y = primaries.white.Y;
+        outPrimaries->white.Z = primaries.white.Z;
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setActiveColorMode(const sp<IBinder>& display, int colorMode) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setActiveColorMode(display, static_cast<ui::ColorMode>(colorMode));
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setBootDisplayMode(const sp<IBinder>& display,
+                                                       int displayModeId) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setBootDisplayMode(display, DisplayModeId{displayModeId});
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::clearBootDisplayMode(const sp<IBinder>& display) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->clearBootDisplayMode(display);
+    }
+    return binderStatusFromStatusT(status);
+}
 
-    status = mFlinger->clearBootDisplayMode(display);
-    return binder::Status::fromStatusT(status);
+binder::Status SurfaceComposerAIDL::getOverlaySupport(gui::OverlayProperties* outProperties) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->getOverlaySupport(outProperties);
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::getBootDisplayModeSupport(bool* outMode) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->getBootDisplayModeSupport(outMode);
+    }
+    return binderStatusFromStatusT(status);
+}
 
-    status = mFlinger->getBootDisplayModeSupport(outMode);
-    return binder::Status::fromStatusT(status);
+binder::Status SurfaceComposerAIDL::getHdrConversionCapabilities(
+        std::vector<gui::HdrConversionCapability>* hdrConversionCapabilities) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->getHdrConversionCapabilities(hdrConversionCapabilities);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setHdrConversionStrategy(
+        const gui::HdrConversionStrategy& hdrConversionStrategy,
+        int32_t* outPreferredHdrOutputType) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setHdrConversionStrategy(hdrConversionStrategy,
+                                                    outPreferredHdrOutputType);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getHdrOutputConversionSupport(bool* outMode) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->getHdrOutputConversionSupport(outMode);
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->setAutoLowLatencyMode(display, on);
     return binder::Status::ok();
 }
 
 binder::Status SurfaceComposerAIDL::setGameContentType(const sp<IBinder>& display, bool on) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
     mFlinger->setGameContentType(display, on);
     return binder::Status::ok();
 }
@@ -7585,7 +8720,7 @@
 binder::Status SurfaceComposerAIDL::captureDisplay(
         const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
     status_t status = mFlinger->captureDisplay(args, captureListener);
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::captureDisplayById(
@@ -7599,60 +8734,375 @@
     } else {
         status = PERMISSION_DENIED;
     }
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::captureLayers(
         const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) {
     status_t status = mFlinger->captureLayers(args, captureListener);
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::overrideHdrTypes(const sp<IBinder>& display,
+                                                     const std::vector<int32_t>& hdrTypes) {
+    // overrideHdrTypes is used by CTS tests, which acquire the necessary
+    // permission dynamically. Don't use the permission cache for this check.
+    status_t status = checkAccessPermission(false);
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+
+    std::vector<ui::Hdr> hdrTypesVector;
+    for (int32_t i : hdrTypes) {
+        hdrTypesVector.push_back(static_cast<ui::Hdr>(i));
+    }
+    status = mFlinger->overrideHdrTypes(display, hdrTypesVector);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) {
+    status_t status;
+    const int uid = IPCThreadState::self()->getCallingUid();
+    if (uid != AID_SYSTEM) {
+        status = PERMISSION_DENIED;
+    } else {
+        status = mFlinger->onPullAtom(atomId, &outPullData->data, &outPullData->success);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
+    if (!outLayers) {
+        return binderStatusFromStatusT(UNEXPECTED_NULL);
+    }
+
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
+        ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
+        return binderStatusFromStatusT(PERMISSION_DENIED);
+    }
+    status_t status = mFlinger->getLayerDebugInfo(outLayers);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getColorManagement(bool* outGetColorManagement) {
+    status_t status = mFlinger->getColorManagement(outGetColorManagement);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) {
+    ui::Dataspace dataspace;
+    ui::PixelFormat pixelFormat;
+    ui::Dataspace wideColorGamutDataspace;
+    ui::PixelFormat wideColorGamutPixelFormat;
+    status_t status =
+            mFlinger->getCompositionPreference(&dataspace, &pixelFormat, &wideColorGamutDataspace,
+                                               &wideColorGamutPixelFormat);
+    if (status == NO_ERROR) {
+        outPref->defaultDataspace = static_cast<int32_t>(dataspace);
+        outPref->defaultPixelFormat = static_cast<int32_t>(pixelFormat);
+        outPref->wideColorGamutDataspace = static_cast<int32_t>(wideColorGamutDataspace);
+        outPref->wideColorGamutPixelFormat = static_cast<int32_t>(wideColorGamutPixelFormat);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDisplayedContentSamplingAttributes(
+        const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+
+    ui::PixelFormat format;
+    ui::Dataspace dataspace;
+    uint8_t componentMask;
+    status = mFlinger->getDisplayedContentSamplingAttributes(display, &format, &dataspace,
+                                                             &componentMask);
+    if (status == NO_ERROR) {
+        outAttrs->format = static_cast<int32_t>(format);
+        outAttrs->dataspace = static_cast<int32_t>(dataspace);
+        outAttrs->componentMask = static_cast<int8_t>(componentMask);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setDisplayContentSamplingEnabled(const sp<IBinder>& display,
+                                                                     bool enable,
+                                                                     int8_t componentMask,
+                                                                     int64_t maxFrames) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setDisplayContentSamplingEnabled(display, enable,
+                                                            static_cast<uint8_t>(componentMask),
+                                                            static_cast<uint64_t>(maxFrames));
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDisplayedContentSample(const sp<IBinder>& display,
+                                                              int64_t maxFrames, int64_t timestamp,
+                                                              gui::DisplayedFrameStats* outStats) {
+    if (!outStats) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+
+    DisplayedFrameStats stats;
+    status = mFlinger->getDisplayedContentSample(display, static_cast<uint64_t>(maxFrames),
+                                                 static_cast<uint64_t>(timestamp), &stats);
+    if (status == NO_ERROR) {
+        // convert from ui::DisplayedFrameStats to gui::DisplayedFrameStats
+        outStats->numFrames = static_cast<int64_t>(stats.numFrames);
+        outStats->component_0_sample.reserve(stats.component_0_sample.size());
+        for (const auto& s : stats.component_0_sample) {
+            outStats->component_0_sample.push_back(static_cast<int64_t>(s));
+        }
+        outStats->component_1_sample.reserve(stats.component_1_sample.size());
+        for (const auto& s : stats.component_1_sample) {
+            outStats->component_1_sample.push_back(static_cast<int64_t>(s));
+        }
+        outStats->component_2_sample.reserve(stats.component_2_sample.size());
+        for (const auto& s : stats.component_2_sample) {
+            outStats->component_2_sample.push_back(static_cast<int64_t>(s));
+        }
+        outStats->component_3_sample.reserve(stats.component_3_sample.size());
+        for (const auto& s : stats.component_3_sample) {
+            outStats->component_3_sample.push_back(static_cast<int64_t>(s));
+        }
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getProtectedContentSupport(bool* outSupported) {
+    status_t status = mFlinger->getProtectedContentSupport(outSupported);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::isWideColorDisplay(const sp<IBinder>& token,
                                                        bool* outIsWideColorDisplay) {
     status_t status = mFlinger->isWideColorDisplay(token, outIsWideColorDisplay);
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::addRegionSamplingListener(
+        const gui::ARect& samplingArea, const sp<IBinder>& stopLayerHandle,
+        const sp<gui::IRegionSamplingListener>& listener) {
+    status_t status = checkReadFrameBufferPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+    android::Rect rect;
+    rect.left = samplingArea.left;
+    rect.top = samplingArea.top;
+    rect.right = samplingArea.right;
+    rect.bottom = samplingArea.bottom;
+    status = mFlinger->addRegionSamplingListener(rect, stopLayerHandle, listener);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeRegionSamplingListener(
+        const sp<gui::IRegionSamplingListener>& listener) {
+    status_t status = checkReadFrameBufferPermission();
+    if (status == OK) {
+        status = mFlinger->removeRegionSamplingListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::addFpsListener(int32_t taskId,
+                                                   const sp<gui::IFpsListener>& listener) {
+    status_t status = checkReadFrameBufferPermission();
+    if (status == OK) {
+        status = mFlinger->addFpsListener(taskId, listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+    status_t status = checkReadFrameBufferPermission();
+    if (status == OK) {
+        status = mFlinger->removeFpsListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::addTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->addTunnelModeEnabledListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->removeTunnelModeEnabledListener(listener);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                               const gui::DisplayModeSpecs& specs) {
+    status_t status = checkAccessPermission();
+    if (status == OK) {
+        status = mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                               gui::DisplayModeSpecs* outSpecs) {
+    if (!outSpecs) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+
+    status = mFlinger->getDesiredDisplayModeSpecs(displayToken, outSpecs);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                                                 bool* outSupport) {
     status_t status = mFlinger->getDisplayBrightnessSupport(displayToken, outSupport);
-    return binder::Status::fromStatusT(status);
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::setDisplayBrightness(const sp<IBinder>& displayToken,
                                                          const gui::DisplayBrightness& brightness) {
     status_t status = checkControlDisplayBrightnessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
-    status = mFlinger->setDisplayBrightness(displayToken, brightness);
-    return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->setDisplayBrightness(displayToken, brightness);
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::addHdrLayerInfoListener(
         const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
     status_t status = checkControlDisplayBrightnessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
-    status = mFlinger->addHdrLayerInfoListener(displayToken, listener);
-    return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->addHdrLayerInfoListener(displayToken, listener);
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::removeHdrLayerInfoListener(
         const sp<IBinder>& displayToken, const sp<gui::IHdrLayerInfoListener>& listener) {
     status_t status = checkControlDisplayBrightnessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
-
-    status = mFlinger->removeHdrLayerInfoListener(displayToken, listener);
-    return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->removeHdrLayerInfoListener(displayToken, listener);
+    }
+    return binderStatusFromStatusT(status);
 }
 
 binder::Status SurfaceComposerAIDL::notifyPowerBoost(int boostId) {
     status_t status = checkAccessPermission();
-    if (status != OK) return binder::Status::fromStatusT(status);
+    if (status == OK) {
+        status = mFlinger->notifyPowerBoost(boostId);
+    }
+    return binderStatusFromStatusT(status);
+}
 
-    status = mFlinger->notifyPowerBoost(boostId);
-    return binder::Status::fromStatusT(status);
+binder::Status SurfaceComposerAIDL::setGlobalShadowSettings(const gui::Color& ambientColor,
+                                                            const gui::Color& spotColor,
+                                                            float lightPosY, float lightPosZ,
+                                                            float lightRadius) {
+    status_t status = checkAccessPermission();
+    if (status != OK) {
+        return binderStatusFromStatusT(status);
+    }
+
+    half4 ambientColorHalf = {ambientColor.r, ambientColor.g, ambientColor.b, ambientColor.a};
+    half4 spotColorHalf = {spotColor.r, spotColor.g, spotColor.b, spotColor.a};
+    status = mFlinger->setGlobalShadowSettings(ambientColorHalf, spotColorHalf, lightPosY,
+                                               lightPosZ, lightRadius);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getDisplayDecorationSupport(
+        const sp<IBinder>& displayToken, std::optional<gui::DisplayDecorationSupport>* outSupport) {
+    std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport> support;
+    status_t status = mFlinger->getDisplayDecorationSupport(displayToken, &support);
+    if (status != NO_ERROR) {
+        ALOGE("getDisplayDecorationSupport failed with error %d", status);
+        return binderStatusFromStatusT(status);
+    }
+
+    if (!support || !support.has_value()) {
+        outSupport->reset();
+    } else {
+        outSupport->emplace();
+        outSupport->value().format = static_cast<int32_t>(support->format);
+        outSupport->value().alphaInterpretation =
+                static_cast<int32_t>(support->alphaInterpretation);
+    }
+
+    return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::setOverrideFrameRate(int32_t uid, float frameRate) {
+    status_t status;
+    const int c_uid = IPCThreadState::self()->getCallingUid();
+    if (c_uid == AID_ROOT || c_uid == AID_SYSTEM) {
+        status = mFlinger->setOverrideFrameRate(uid, frameRate);
+    } else {
+        ALOGE("setOverrideFrameRate() permission denied for uid: %d", c_uid);
+        status = PERMISSION_DENIED;
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
+    *outPriority = mFlinger->getGpuContextPriority();
+    return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::getMaxAcquiredBufferCount(int32_t* buffers) {
+    status_t status = mFlinger->getMaxAcquiredBufferCount(buffers);
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::addWindowInfosListener(
+        const sp<gui::IWindowInfosListener>& windowInfosListener) {
+    status_t status;
+    const int pid = IPCThreadState::self()->getCallingPid();
+    const int uid = IPCThreadState::self()->getCallingUid();
+    // TODO(b/270566761) update permissions check so that only system_server and shell can add
+    // WindowInfosListeners
+    if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
+        checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+        status = mFlinger->addWindowInfosListener(windowInfosListener);
+    } else {
+        status = PERMISSION_DENIED;
+    }
+    return binderStatusFromStatusT(status);
+}
+
+binder::Status SurfaceComposerAIDL::removeWindowInfosListener(
+        const sp<gui::IWindowInfosListener>& windowInfosListener) {
+    status_t status;
+    const int pid = IPCThreadState::self()->getCallingPid();
+    const int uid = IPCThreadState::self()->getCallingUid();
+    if (uid == AID_SYSTEM || uid == AID_GRAPHICS ||
+        checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+        status = mFlinger->removeWindowInfosListener(windowInfosListener);
+    } else {
+        status = PERMISSION_DENIED;
+    }
+    return binderStatusFromStatusT(status);
 }
 
 status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
@@ -7677,6 +9127,30 @@
     return OK;
 }
 
+status_t SurfaceComposerAIDL::checkReadFrameBufferPermission() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if ((uid != AID_GRAPHICS) && !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) {
+        ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid);
+        return PERMISSION_DENIED;
+    }
+    return OK;
+}
+
+void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
+    static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
+}
+
+const DisplayDevice* SurfaceFlinger::getDisplayFromLayerStack(ui::LayerStack layerStack) {
+    for (const auto& [_, display] : mDisplays) {
+        if (display->getLayerStack() == layerStack) {
+            return display.get();
+        }
+    }
+    return nullptr;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d9add5c..0bc506f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -25,15 +25,18 @@
 #include <android/gui/BnSurfaceComposer.h>
 #include <android/gui/DisplayStatInfo.h>
 #include <android/gui/DisplayState.h>
+#include <android/gui/ISurfaceComposerClient.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
+#include <ftl/algorithm.h>
 #include <ftl/future.h>
-#include <ftl/small_map.h>
+#include <ftl/non_null.h>
 #include <gui/BufferQueue.h>
+#include <gui/CompositorTiming.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
-#include <gui/ISurfaceComposerClient.h>
 #include <gui/ITransactionCompletedListener.h>
+#include <gui/LayerDebugInfo.h>
 #include <gui/LayerState.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
@@ -50,26 +53,36 @@
 #include <utils/Trace.h>
 #include <utils/threads.h>
 
-#include <compositionengine/FenceResult.h>
 #include <compositionengine/OutputColorSetting.h>
 #include <scheduler/Fps.h>
+#include <scheduler/PresentLatencyTracker.h>
+#include <scheduler/Time.h>
+#include <scheduler/TransactionSchedule.h>
+#include <scheduler/interface/CompositionCoverage.h>
+#include <scheduler/interface/ICompositor.h>
+#include <ui/FenceResult.h>
 
-#include "ClientCache.h"
+#include "Display/DisplayMap.h"
+#include "Display/PhysicalDisplay.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWC2.h"
 #include "DisplayHardware/PowerAdvisor.h"
 #include "DisplayIdGenerator.h"
 #include "Effects/Daltonizer.h"
 #include "FlagManager.h"
-#include "FrameTracker.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshot.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
+#include "FrontEnd/TransactionHandler.h"
 #include "LayerVector.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/ISchedulerCallback.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/RefreshRateStats.h"
 #include "Scheduler/Scheduler.h"
-#include "Scheduler/VsyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "ThreadContext.h"
-#include "TracedOrdinal.h"
 #include "Tracing/LayerTracing.h"
 #include "Tracing/TransactionTracing.h"
 #include "TransactionCallbackInvoker.h"
@@ -90,14 +103,16 @@
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
+#include <vector>
 
 #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
+#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
+#include "Client.h"
 
 using namespace android::surfaceflinger;
 
 namespace android {
 
-class Client;
 class EventThread;
 class FlagManager;
 class FpsReporter;
@@ -115,6 +130,8 @@
 class ScreenCapturer;
 class WindowInfosListenerInvoker;
 
+using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
+using frontend::TransactionHandler;
 using gui::CaptureArgs;
 using gui::DisplayCaptureArgs;
 using gui::IRegionSamplingListener;
@@ -146,7 +163,8 @@
     eDisplayTransactionNeeded = 0x04,
     eTransformHintUpdateNeeded = 0x08,
     eTransactionFlushNeeded = 0x10,
-    eTransactionMask = 0x1f,
+    eInputInfoUpdateNeeded = 0x20,
+    eTransactionMask = 0x3f,
 };
 
 // Latch Unsignaled buffer behaviours
@@ -157,33 +175,20 @@
     // Latch unsignaled is permitted when a single layer is updated in a frame,
     // and the update includes just a buffer update (i.e. no sync transactions
     // or geometry changes).
+    // Latch unsignaled is also only permitted when a single transaction is ready
+    // to be applied. If we pass an unsignaled fence to HWC, HWC might miss presenting
+    // the frame if the fence does not fire in time. If we apply another transaction,
+    // we may penalize the other transaction unfairly.
     AutoSingleLayer,
 
     // All buffers are latched unsignaled. This behaviour is discouraged as it
     // can break sync transactions, stall the display and cause undesired side effects.
+    // This is equivalent to ignoring the acquire fence when applying transactions.
     Always,
 };
 
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
-struct SurfaceFlingerBE {
-    // protected by mCompositorTimingLock;
-    mutable std::mutex mCompositorTimingLock;
-    CompositorTiming mCompositorTiming;
-
-    // Only accessed from the main thread.
-    struct CompositePresentTime {
-        nsecs_t composite = -1;
-        std::shared_ptr<FenceTime> display = FenceTime::NO_FENCE;
-    };
-    std::queue<CompositePresentTime> mCompositePresentTimes;
-
-    static const size_t NUM_BUCKETS = 8; // < 1-7, 7+
-    nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
-    nsecs_t mTotalTime = 0;
-    std::atomic<nsecs_t> mLastSwapTime = 0;
-};
-
 class SurfaceFlinger : public BnSurfaceComposer,
                        public PriorityDumper,
                        private IBinder::DeathRecipient,
@@ -204,29 +209,6 @@
 
     static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; }
 
-    // This is the phase offset in nanoseconds of the software vsync event
-    // relative to the vsync event reported by HWComposer.  The software vsync
-    // event is when SurfaceFlinger and Choreographer-based applications run each
-    // frame.
-    //
-    // This phase offset allows adjustment of the minimum latency from application
-    // wake-up time (by Choreographer) to the time at which the resulting window
-    // image is displayed.  This value may be either positive (after the HW vsync)
-    // or negative (before the HW vsync). Setting it to 0 will result in a lower
-    // latency bound of two vsync periods because the app and SurfaceFlinger
-    // will run just after the HW vsync.  Setting it to a positive number will
-    // result in the minimum latency being:
-    //
-    //     (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
-    //
-    // Note that reducing this latency makes it more likely for the applications
-    // to not have their window content image ready in time.  When this happens
-    // the latency will end up being an additional vsync period, and animations
-    // will hiccup.  Therefore, this latency should be tuned somewhat
-    // conservatively (or at least with awareness of the trade-off being made).
-    static int64_t vsyncPhaseOffsetNs;
-    static int64_t sfVsyncPhaseOffsetNs;
-
     // If fences from sync Framework are supported.
     static bool hasSyncFramework;
 
@@ -249,10 +231,6 @@
     static uint32_t maxGraphicsWidth;
     static uint32_t maxGraphicsHeight;
 
-    // Indicate if a device has wide color gamut display. This is typically
-    // found on devices with wide color gamut (e.g. Display-P3) display.
-    static bool hasWideColorDisplay;
-
     // Indicate if device wants color management on its display.
     static const constexpr bool useColorManagement = true;
 
@@ -280,9 +258,6 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    SurfaceFlingerBE& getBE() { return mBE; }
-    const SurfaceFlingerBE& getBE() const { return mBE; }
-
     // Indicates frame activity, i.e. whether commit and/or composite is taking place.
     enum class FrameHint { kNone, kActive };
 
@@ -309,9 +284,6 @@
 
     renderengine::RenderEngine& getRenderEngine() const;
 
-    bool authenticateSurfaceTextureLocked(
-        const sp<IGraphicBufferProducer>& bufferProducer) const;
-
     void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
     void onLayerUpdate();
@@ -319,23 +291,20 @@
     void removeHierarchyFromOffscreenLayers(Layer* layer);
     void removeFromOffscreenLayers(Layer* layer);
 
-    // TODO: Remove atomic if move dtor to main thread CL lands
-    std::atomic<uint32_t> mNumClones;
+    // 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);
+
+    std::vector<Layer*> mLayerMirrorRoots;
 
     TransactionCallbackInvoker& getTransactionCallbackInvoker() {
         return mTransactionCallbackInvoker;
     }
 
-    // Converts from a binder handle to a Layer
-    // Returns nullptr if the handle does not point to an existing layer.
-    // Otherwise, returns a weak reference so that callers off the main-thread
-    // won't accidentally hold onto the last strong reference.
-    wp<Layer> fromHandle(const sp<IBinder>& handle) const;
-
     // If set, disables reusing client composition buffers. This can be set by
     // debug.sf.disable_client_composition_cache
     bool mDisableClientCompositionCache = false;
-    void windowInfosReported();
 
     // Disables expensive rendering for all displays
     // This is scheduled on the main thread
@@ -353,6 +322,23 @@
     // on this behavior to increase contrast for some media sources.
     bool mTreat170mAsSrgb = false;
 
+    // Allows to ignore physical orientation provided through hwc API in favour of
+    // 'ro.surface_flinger.primary_display_orientation'.
+    // TODO(b/246793311): Clean up a temporary property
+    bool mIgnoreHwcPhysicalDisplayOrientation = false;
+
+    void forceFutureUpdate(int delayInMs);
+    const DisplayDevice* getDisplayFromLayerStack(ui::LayerStack)
+            REQUIRES(mStateLock, kMainThreadContext);
+
+    // TODO (b/259407931): Remove.
+    // TODO (b/281857977): This should be annotated with REQUIRES(kMainThreadContext), but this
+    // would require thread safety annotations throughout the frontend (in particular Layer and
+    // LayerFE).
+    static ui::Transform::RotationFlags getActiveDisplayRotationFlags() {
+        return sActiveDisplayRotationFlags;
+    }
+
 protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
@@ -361,7 +347,7 @@
             REQUIRES(mStateLock);
 
     virtual std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
-            const BufferData& bufferData, const char* layerName) const;
+            BufferData& bufferData, const char* layerName, uint64_t transactionId);
 
     // Returns true if any display matches a `bool(const DisplayDevice&)` predicate.
     template <typename Predicate>
@@ -375,34 +361,28 @@
 
 private:
     friend class BufferLayer;
-    friend class BufferQueueLayer;
-    friend class BufferStateLayer;
     friend class Client;
     friend class FpsReporter;
     friend class TunnelModeEnabledReporter;
     friend class Layer;
-    friend class MonitoredProducer;
     friend class RefreshRateOverlay;
     friend class RegionSamplingThread;
     friend class LayerRenderArea;
     friend class LayerTracing;
+    friend class SurfaceComposerAIDL;
+    friend class DisplayRenderArea;
 
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class TransactionApplicationTest;
     friend class TunnelModeEnabledReporterTest;
 
-    using VsyncModulator = scheduler::VsyncModulator;
     using TransactionSchedule = scheduler::TransactionSchedule;
-    using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+    using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
     using RenderAreaFuture = ftl::Future<std::unique_ptr<RenderArea>>;
     using DumpArgs = Vector<String16>;
     using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
 
-    // This value is specified in number of frames.  Log frame stats at most
-    // every half hour.
-    enum { LOG_FRAME_STATS_PERIOD =  30*60*60 };
-
     class State {
     public:
         explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {}
@@ -422,7 +402,20 @@
 
         const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
         LayerVector layersSortedByZ;
-        DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
+
+        // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap.
+        DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays;
+
+        std::optional<size_t> getDisplayIndex(PhysicalDisplayId displayId) const {
+            for (size_t i = 0; i < displays.size(); i++) {
+                const auto& state = displays.valueAt(i);
+                if (state.physical && state.physical->id == displayId) {
+                    return i;
+                }
+            }
+
+            return {};
+        }
 
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
@@ -471,21 +464,12 @@
                 mCounterByLayerHandle GUARDED_BY(mLock);
     };
 
-    using ActiveModeInfo = DisplayDevice::ActiveModeInfo;
-    using KernelIdleTimerController =
-            ::android::scheduler::RefreshRateConfigs::KernelIdleTimerController;
-
     enum class BootStage {
         BOOTLOADER,
         BOOTANIMATION,
         FINISHED,
     };
 
-    struct HotplugEvent {
-        hal::HWDisplayId hwcDisplayId;
-        hal::Connection connection = hal::Connection::INVALID;
-    };
-
     template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
     static Dumper dumper(F&& dump) {
         using namespace std::placeholders;
@@ -510,19 +494,11 @@
         return std::bind(dump, this, _1, _2, _3);
     }
 
-    template <typename... Args,
-              typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
-    void modulateVsync(Handler handler, Args... args) {
-        if (const auto config = (*mVsyncModulator.*handler)(args...)) {
-            const auto vsyncPeriod = mScheduler->getVsyncPeriodFromRefreshRateConfigs();
-            setVsyncConfig(*config, vsyncPeriod);
-        }
-    }
-
-    static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
     // Maximum allowed number of display frames that can be set through backdoor
     static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
 
+    static const size_t MAX_LAYERS = 4096;
+
     // Implements IBinder.
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
     status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
@@ -530,32 +506,29 @@
             EXCLUDES(mStateLock);
 
     // Implements ISurfaceComposer
-    sp<ISurfaceComposerClient> createConnection() override;
-    sp<IBinder> createDisplay(const String8& displayName, bool secure);
+    sp<IBinder> createDisplay(const String8& displayName, bool secure,
+                              float requestedRefreshRate = 0.0f);
     void destroyDisplay(const sp<IBinder>& displayToken);
     std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) {
         Mutex::Autolock lock(mStateLock);
         return getPhysicalDisplayIdsLocked();
     }
-    status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const EXCLUDES(mStateLock);
 
     sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
-    status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                                 const Vector<ComposerState>& state,
-                                 const Vector<DisplayState>& displays, uint32_t flags,
-                                 const sp<IBinder>& applyToken,
-                                 const InputWindowCommands& inputWindowCommands,
-                                 int64_t desiredPresentTime, bool isAutoTimestamp,
-                                 const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-                                 const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                 uint64_t transactionId) override;
-    void bootFinished() override;
-    bool authenticateSurfaceTexture(
-            const sp<IGraphicBufferProducer>& bufferProducer) const override;
-    status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const override;
+    status_t setTransactionState(
+            const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
+            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+            bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+            uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
+    void bootFinished();
+    virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
     sp<IDisplayEventConnection> createDisplayEventConnection(
-            ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
-            ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
+            gui::ISurfaceComposer::VsyncSource vsyncSource =
+                    gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+            EventRegistrationFlags eventRegistration = {},
+            const sp<IBinder>& layerHandle = nullptr);
 
     status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
     status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
@@ -564,63 +537,55 @@
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
     status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
             EXCLUDES(mStateLock);
-    status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
-            EXCLUDES(mStateLock) override;
-    status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
-            EXCLUDES(mStateLock) override;
-    status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
-                                       ui::DisplayPrimaries&) override;
-    status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+    status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
+            EXCLUDES(mStateLock);
+    status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                            ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+    void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+                                       const display::DisplaySnapshot&);
+    status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
+    status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
     status_t getBootDisplayModeSupport(bool* outSupport) const;
-    status_t setBootDisplayMode(const sp<IBinder>& displayToken, ui::DisplayModeId id) override;
+    status_t setBootDisplayMode(const sp<display::DisplayToken>&, DisplayModeId);
+    status_t getOverlaySupport(gui::OverlayProperties* outProperties) const;
     status_t clearBootDisplayMode(const sp<IBinder>& displayToken);
+    status_t getHdrConversionCapabilities(
+            std::vector<gui::HdrConversionCapability>* hdrConversionCapaabilities) const;
+    status_t setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy,
+                                      int32_t*);
+    status_t getHdrOutputConversionSupport(bool* outSupport) const;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on);
     void setGameContentType(const sp<IBinder>& displayToken, bool on);
     void setPowerMode(const sp<IBinder>& displayToken, int mode);
-    status_t clearAnimationFrameStats() override;
-    status_t getAnimationFrameStats(FrameStats* outStats) const override;
     status_t overrideHdrTypes(const sp<IBinder>& displayToken,
-                              const std::vector<ui::Hdr>& hdrTypes) override;
-    status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) override;
-    status_t enableVSyncInjections(bool enable) override;
-    status_t injectVSync(nsecs_t when) override;
-    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
-    status_t getColorManagement(bool* outGetColorManagement) const override;
+                              const std::vector<ui::Hdr>& hdrTypes);
+    status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
+    status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
+    status_t getColorManagement(bool* outGetColorManagement) const;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
                                       ui::Dataspace* outWideColorGamutDataspace,
-                                      ui::PixelFormat* outWideColorGamutPixelFormat) const override;
+                                      ui::PixelFormat* outWideColorGamutPixelFormat) const;
     status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                    ui::PixelFormat* outFormat,
                                                    ui::Dataspace* outDataspace,
-                                                   uint8_t* outComponentMask) const override;
+                                                   uint8_t* outComponentMask) const;
     status_t setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken, bool enable,
-                                              uint8_t componentMask, uint64_t maxFrames) override;
+                                              uint8_t componentMask, uint64_t maxFrames);
     status_t getDisplayedContentSample(const sp<IBinder>& displayToken, uint64_t maxFrames,
-                                       uint64_t timestamp,
-                                       DisplayedFrameStats* outStats) const override;
-    status_t getProtectedContentSupport(bool* outSupported) const override;
+                                       uint64_t timestamp, DisplayedFrameStats* outStats) const;
+    status_t getProtectedContentSupport(bool* outSupported) const;
     status_t isWideColorDisplay(const sp<IBinder>& displayToken, bool* outIsWideColorDisplay) const;
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
-                                       const sp<IRegionSamplingListener>& listener) override;
-    status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
-    status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override;
-    status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
-    status_t addTunnelModeEnabledListener(
-            const sp<gui::ITunnelModeEnabledListener>& listener) override;
-    status_t removeTunnelModeEnabledListener(
-            const sp<gui::ITunnelModeEnabledListener>& listener) override;
+                                       const sp<IRegionSamplingListener>& listener);
+    status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+    status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener);
+    status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
+    status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
+    status_t removeTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                        ui::DisplayModeId displayModeId, bool allowGroupSwitching,
-                                        float primaryRefreshRateMin, float primaryRefreshRateMax,
-                                        float appRequestRefreshRateMin,
-                                        float appRequestRefreshRateMax) override;
-    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                        ui::DisplayModeId* outDefaultMode,
-                                        bool* outAllowGroupSwitching,
-                                        float* outPrimaryRefreshRateMin,
-                                        float* outPrimaryRefreshRateMax,
-                                        float* outAppRequestRefreshRateMin,
-                                        float* outAppRequestRefreshRateMax) override;
+                                        const gui::DisplayModeSpecs&);
+    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs*);
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken,
                                   const gui::DisplayBrightness& brightness);
@@ -630,36 +595,32 @@
                                         const sp<gui::IHdrLayerInfoListener>& listener);
     status_t notifyPowerBoost(int32_t boostId);
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
-                                     float lightPosY, float lightPosZ, float lightRadius) override;
+                                     float lightPosY, float lightPosZ, float lightRadius);
     status_t getDisplayDecorationSupport(
             const sp<IBinder>& displayToken,
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
-                    outSupport) const override;
+                    outSupport) const;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
-                          int8_t compatibility, int8_t changeFrameRateStrategy) override;
+                          int8_t compatibility, int8_t changeFrameRateStrategy);
 
     status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
-                                  const FrameTimelineInfo& frameTimelineInfo) override;
+                                  const gui::FrameTimelineInfo& frameTimelineInfo);
 
-    status_t setOverrideFrameRate(uid_t uid, float frameRate) override;
+    status_t setOverrideFrameRate(uid_t uid, float frameRate);
 
-    status_t addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) override;
+    int getGpuContextPriority();
 
-    int getGPUContextPriority() override;
+    status_t getMaxAcquiredBufferCount(int* buffers) const;
 
-    status_t getMaxAcquiredBufferCount(int* buffers) const override;
-
-    status_t addWindowInfosListener(
-            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+    status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
     status_t removeWindowInfosListener(
-            const sp<gui::IWindowInfosListener>& windowInfosListener) const override;
+            const sp<gui::IWindowInfosListener>& windowInfosListener) const;
 
     // Implements IBinder::DeathRecipient.
     void binderDied(const wp<IBinder>& who) override;
 
     // HWC2::ComposerCallback overrides:
-    void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
+    void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp,
                             std::optional<hal::VsyncPeriodNanos>) override;
     void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override;
     void onComposerHalRefresh(hal::HWDisplayId) override;
@@ -667,34 +628,28 @@
                                                const hal::VsyncPeriodChangeTimeline&) override;
     void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
     void onComposerHalVsyncIdle(hal::HWDisplayId) override;
+    void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
 
     // ICompositor overrides:
-
-    // Commits transactions for layers and displays. Returns whether any state has been invalidated,
-    // i.e. whether a frame should be composited for each display.
-    bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) override;
-
-    // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
-    // via RenderEngine and the Composer HAL, respectively.
-    void composite(nsecs_t frameTime, int64_t vsyncId) override;
-
-    // Samples the composited frame via RegionSamplingThread.
+    void configure() override;
+    bool commit(TimePoint frameTime, VsyncId, TimePoint expectedVsyncTime) override;
+    void composite(TimePoint frameTime, VsyncId) override;
     void sample() override;
 
-    /*
-     * ISchedulerCallback
-     */
+    // ISchedulerCallback overrides:
 
     // Toggles hardware VSYNC by calling into HWC.
-    void setVsyncEnabled(bool) override;
-    // Sets the desired display mode if allowed by policy.
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override;
-    // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+    // TODO(b/241286146): Rename for self-explanatory API.
+    void setVsyncEnabled(PhysicalDisplayId, bool) override;
+    void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
     void kernelTimerChanged(bool expired) override;
-    // Called when the frame rate override list changed to trigger an event.
     void triggerOnFrameRateOverridesChanged() override;
+
     // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
     void toggleKernelIdleTimer() REQUIRES(mStateLock);
+
+    using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
+
     // Get the controller and timeout that will help decide how the kernel idle timer will be
     // configured and what value to use as the timeout.
     std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
@@ -708,93 +663,115 @@
     bool mKernelIdleTimerEnabled = false;
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
+    // Show render rate with refresh rate overlay
+    bool mRefreshRateOverlayRenderRate = false;
+    // Show render rate overlay offseted to the middle of the screen (e.g. for circular displays)
+    bool mRefreshRateOverlayShowInMiddle = false;
 
-    // Called on the main thread in response to initializeDisplays()
-    void onInitializeDisplays() REQUIRES(mStateLock);
-    // Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
-    void setDesiredActiveMode(const ActiveModeInfo& info, bool force = false) REQUIRES(mStateLock);
-    status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+    void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
+            REQUIRES(mStateLock);
+
+    status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
     // Sets the active mode and a new refresh rate in SF.
-    void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
+    void updateInternalStateWithChangedMode() REQUIRES(mStateLock, kMainThreadContext);
     // Calls to setActiveMode on the main thread if there is a pending mode change
     // that needs to be applied.
-    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
+    void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock, kMainThreadContext);
     void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called when active mode is no longer is progress
     void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
     // Called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
-            REQUIRES(mStateLock);
+            REQUIRES(mStateLock, kMainThreadContext);
 
-    // Returns true if the display has a visible HDR layer in its layer stack.
-    bool hasVisibleHdrLayer(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
+    // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that
+    // display. Falls back to the display's defaultModeId otherwise.
+    ftl::Optional<scheduler::FrameRateMode> getPreferredDisplayMode(
+            PhysicalDisplayId, DisplayModeId defaultModeId) const REQUIRES(mStateLock);
 
-    // Sets the desired display mode specs.
     status_t setDesiredDisplayModeSpecsInternal(
-            const sp<DisplayDevice>& display,
-            const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
-            EXCLUDES(mStateLock);
+            const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
+            EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
 
-    status_t applyRefreshRateConfigsPolicy(const sp<DisplayDevice>&, bool force = false)
-            REQUIRES(mStateLock);
+    // TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
+    status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
+                                            const scheduler::RefreshRateSelector&,
+                                            bool force = false)
+            REQUIRES(mStateLock, kMainThreadContext);
 
-    void commitTransactions() EXCLUDES(mStateLock);
-    void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+    void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+    void commitTransactionsLocked(uint32_t transactionFlags)
+            REQUIRES(mStateLock, kMainThreadContext);
     void doCommitTransactions() REQUIRES(mStateLock);
 
     // Returns whether a new buffer has been latched.
     bool latchBuffers();
 
     void updateLayerGeometry();
+    void updateLayerMetadataSnapshot();
+    std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
+            compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly,
+            int64_t vsyncId);
+    void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
+                                          std::vector<std::pair<Layer*, LayerFE*>>& layers);
+    bool updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
+                                    bool transactionsFlushed, bool& out)
+            REQUIRES(kMainThreadContext);
+    bool updateLayerSnapshots(VsyncId vsyncId, frontend::Update& update, bool transactionsFlushed,
+                              bool& out) REQUIRES(kMainThreadContext);
+    void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
+    frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
 
-    void updateInputFlinger();
+    void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
     void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
     void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
                           std::vector<gui::DisplayInfo>& outDisplayInfos);
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void updateCursorAsync();
 
-    void initScheduler(const sp<DisplayDevice>& display) REQUIRES(mStateLock);
-    void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
-    void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
+    void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
 
+    void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext);
+    void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
 
     /*
      * Transactions
      */
-    bool applyTransactionState(const FrameTimelineInfo& info, Vector<ComposerState>& state,
+    bool applyTransactionState(const FrameTimelineInfo& info,
+                               std::vector<ResolvedComposerState>& state,
                                Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime, bool isAutoTimestamp,
-                               const client_cache_t& uncacheBuffer, const int64_t postTime,
-                               uint32_t permissions, bool hasListenerCallbacks,
+                               const std::vector<uint64_t>& uncacheBufferIds,
+                               const int64_t postTime, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
                                int originPid, int originUid, uint64_t transactionId)
             REQUIRES(mStateLock);
-    // flush pending transaction that was presented after desiredPresentTime.
-    bool flushTransactionQueues(int64_t vsyncId);
+    // Flush pending transactions that were presented after desiredPresentTime.
+    // For test only
+    bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
 
-    std::vector<TransactionState> flushTransactions();
+    bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
+    bool applyAndCommitDisplayTransactionStates(std::vector<TransactionState>& transactions)
+            REQUIRES(kMainThreadContext);
 
     // Returns true if there is at least one transaction that needs to be flushed
     bool transactionFlushNeeded();
+    void addTransactionReadyFilters();
+    TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
+    TransactionHandler::TransactionReadiness transactionReadyBufferCheck(
+            const TransactionHandler::TransactionFlushState& flushState)
+            REQUIRES(kMainThreadContext);
 
-    int flushPendingTransactionQueues(
-            std::vector<TransactionState>& transactions,
-            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions,
-            bool tryApplyUnsignaled) REQUIRES(mStateLock, mQueueLock);
-
-    int flushUnsignaledPendingTransactionQueues(
-            std::vector<TransactionState>& transactions,
-            std::unordered_map<sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            std::unordered_set<sp<IBinder>, SpHash<IBinder>>& applyTokensWithUnsignaledTransactions)
-            REQUIRES(mStateLock, mQueueLock);
-
-    uint32_t setClientStateLocked(const FrameTimelineInfo&, ComposerState&,
+    uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&,
                                   int64_t desiredPresentTime, bool isAutoTimestamp,
-                                  int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
-
+                                  int64_t postTime, uint64_t transactionId) REQUIRES(mStateLock);
+    uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
+                                          int64_t desiredPresentTime, bool isAutoTimestamp,
+                                          int64_t postTime, uint64_t transactionId)
+            REQUIRES(mStateLock);
     uint32_t getTransactionFlags() const;
 
     // Sets the masked bits, and schedules a commit if needed.
@@ -807,41 +784,20 @@
 
     void commitOffscreenLayers();
 
-    enum class TransactionReadiness {
-        NotReady,
-        NotReadyBarrier,
-        Ready,
-        ReadyUnsignaled,
-    };
-    TransactionReadiness transactionIsReadyToBeApplied(TransactionState& state,
-            const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
-            uid_t originUid, const Vector<ComposerState>& states,
-            const std::unordered_map<
-                sp<IBinder>, uint64_t, SpHash<IBinder>>& bufferLayersReadyToPresent,
-            size_t totalTXapplied, bool tryApplyUnsignaled) const REQUIRES(mStateLock);
     static LatchUnsignaledConfig getLatchUnsignaledConfig();
     bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
-                               size_t totalTXapplied) const;
-    bool stopTransactionProcessing(const std::unordered_set<sp<IBinder>, SpHash<IBinder>>&
-                                           applyTokensWithUnsignaledTransactions) const;
-    bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
-    bool applyTransactionsLocked(std::vector<TransactionState>& transactions, int64_t vsyncId)
+                               bool firstTransaction) const;
+    bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
             REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
-    bool frameIsEarly(nsecs_t expectedPresentTime, int64_t vsyncId) const;
+    bool frameIsEarly(TimePoint expectedPresentTime, VsyncId) const;
+
     /*
      * Layer management
      */
-    status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
-                         const sp<IBinder>& parentHandle, int32_t* outLayerId,
-                         const sp<Layer>& parentLayer = nullptr,
-                         uint32_t* outTransformHint = nullptr);
-
-    status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
-                                    sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
-                                    sp<Layer>* outLayer);
+    status_t createLayer(LayerCreationArgs& args, gui::CreateSurfaceResult& outResult);
 
     status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
                                     sp<Layer>* outLayer);
@@ -849,21 +805,17 @@
     status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
                                sp<Layer>* outLayer);
 
-    status_t createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
-                                  sp<Layer>* outLayer);
-
     status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
-                         sp<IBinder>* outHandle, int32_t* outLayerId);
+                         gui::CreateSurfaceResult& outResult);
 
-    // called when all clients have released all their references to
-    // this layer meaning it is entirely safe to destroy all
-    // resources associated to this layer.
-    void onHandleDestroyed(BBinder* handle, sp<Layer>& layer);
+    status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args,
+                           gui::CreateSurfaceResult& outResult);
+
     void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock);
 
     // add a layer to SurfaceFlinger
-    status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
-                            const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+    status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
+                            const sp<Layer>& layer, const wp<Layer>& parentLayer,
                             uint32_t* outTransformHint);
 
     // Traverse through all the layers and compute and cache its bounds.
@@ -872,22 +824,25 @@
     // Boot animation, on/off animations and screen capture
     void startBootAnim();
 
-    ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+    ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction,
                                                        ui::Size bufferSize, ui::PixelFormat,
                                                        bool allowProtected, bool grayscale,
                                                        const sp<IScreenCaptureListener>&);
     ftl::SharedFuture<FenceResult> captureScreenCommon(
-            RenderAreaFuture, TraverseLayersFunction,
+            RenderAreaFuture, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
             bool grayscale, const sp<IScreenCaptureListener>&);
     ftl::SharedFuture<FenceResult> renderScreenImpl(
-            const RenderArea&, TraverseLayersFunction,
+            std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
             const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
-            bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock);
+            bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
+            REQUIRES(kMainThreadContext);
 
     // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
     // matching ownerUid
-    void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&);
+    void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid,
+                                    std::unordered_set<uint32_t> excludeLayerIds,
+                                    const LayerVector::Visitor&);
 
     void readPersistentProperties();
 
@@ -896,8 +851,9 @@
     /*
      * Display and layer stack management
      */
-    // called when starting, or restarting after system_server death
-    void initializeDisplays();
+
+    // Called during boot, and restart after system_server death.
+    void initializeDisplays() REQUIRES(kMainThreadContext);
 
     sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
             REQUIRES(mStateLock) {
@@ -905,8 +861,9 @@
     }
 
     sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
-        const sp<DisplayDevice> nullDisplay;
-        return mDisplays.get(displayToken).value_or(std::cref(nullDisplay));
+        return mDisplays.get(displayToken)
+                .or_else(ftl::static_ref<sp<DisplayDevice>>([] { return nullptr; }))
+                .value();
     }
 
     sp<const DisplayDevice> getDisplayDeviceLocked(PhysicalDisplayId id) const
@@ -933,12 +890,12 @@
     }
 
     sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
-        if (const auto display = getDisplayDeviceLocked(mActiveDisplayToken)) {
+        if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
             return display;
         }
         // The active display is outdated, so fall back to the primary display.
-        mActiveDisplayToken.clear();
-        return getDisplayDeviceLocked(getPrimaryDisplayTokenLocked());
+        mActiveDisplayId = getPrimaryDisplayIdLocked();
+        return getDisplayDeviceLocked(mActiveDisplayId);
     }
 
     sp<const DisplayDevice> getDefaultDisplayDevice() const EXCLUDES(mStateLock) {
@@ -946,6 +903,21 @@
         return getDefaultDisplayDeviceLocked();
     }
 
+    using DisplayDeviceAndSnapshot =
+            std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>;
+
+    // Combinator for ftl::Optional<PhysicalDisplay>::and_then.
+    auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) {
+        return [this](const display::PhysicalDisplay& display) REQUIRES(
+                       mStateLock) -> ftl::Optional<DisplayDeviceAndSnapshot> {
+            if (auto device = getDisplayDeviceLocked(display.snapshot().displayId())) {
+                return std::make_pair(std::move(device), display.snapshotRef());
+            }
+
+            return {};
+        };
+    }
+
     // Returns the first display that matches a `bool(const DisplayDevice&)` predicate.
     template <typename Predicate>
     sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
@@ -959,10 +931,15 @@
 
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
-    void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
+    void invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty);
 
-    bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
-        return display->getDisplayToken() == mActiveDisplayToken;
+    ui::LayerFilter makeLayerFilterForDisplay(DisplayId displayId, ui::LayerStack layerStack)
+            REQUIRES(mStateLock) {
+        return {layerStack,
+                PhysicalDisplayId::tryCast(displayId)
+                        .and_then(display::getPhysicalDisplay(mPhysicalDisplays))
+                        .transform(&display::PhysicalDisplay::isInternal)
+                        .value_or(false)};
     }
 
     /*
@@ -979,14 +956,7 @@
     /*
      * Compositing
      */
-    void postComposition();
-    void getCompositorTiming(CompositorTiming* compositorTiming);
-    void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
-                                std::shared_ptr<FenceTime>& presentFenceTime);
-    void setCompositorTimingSnapped(const DisplayStatInfo& stats,
-                                    nsecs_t compositeToPresentLatency);
-
-    void postFrame() REQUIRES(kMainThreadContext);
+    void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
 
     /*
      * Display management
@@ -994,18 +964,31 @@
     std::pair<DisplayModes, DisplayModePtr> loadDisplayModes(PhysicalDisplayId) const
             REQUIRES(mStateLock);
 
+    // TODO(b/241285876): Move to DisplayConfigurator.
+    //
+    // Returns whether displays have been added/changed/removed, i.e. whether ICompositor should
+    // commit display transactions.
+    bool configureLocked() REQUIRES(mStateLock) REQUIRES(kMainThreadContext)
+            EXCLUDES(mHotplugMutex);
+
+    // Returns a string describing the hotplug, or nullptr if it was rejected.
+    const char* processHotplug(PhysicalDisplayId, hal::HWDisplayId, bool connected,
+                               DisplayIdentificationInfo&&) REQUIRES(mStateLock)
+            REQUIRES(kMainThreadContext);
+
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
             const wp<IBinder>& displayToken,
             std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
-    void processDisplayChangesLocked() REQUIRES(mStateLock);
-    void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+    void processDisplayChangesLocked() REQUIRES(mStateLock, kMainThreadContext);
+    void processDisplayRemoved(const wp<IBinder>& displayToken)
+            REQUIRES(mStateLock, kMainThreadContext);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
-                               const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
-    void processDisplayHotplugEventsLocked() REQUIRES(mStateLock);
+                               const DisplayDeviceState& drawingState)
+            REQUIRES(mStateLock, kMainThreadContext);
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
@@ -1014,55 +997,38 @@
      */
     nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
 
-    void setHWCVsyncEnabled(PhysicalDisplayId id, hal::Vsync enabled) {
-        mLastHWCVsyncState = enabled;
-        getHwComposer().setVsyncEnabled(id, enabled);
+    void setHWCVsyncEnabled(PhysicalDisplayId id, bool enabled) {
+        hal::Vsync halState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
+        getHwComposer().setVsyncEnabled(id, halState);
     }
 
-    struct FenceWithFenceTime {
-        sp<Fence> fence = Fence::NO_FENCE;
-        std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
-    };
+    using FenceTimePtr = std::shared_ptr<FenceTime>;
 
-    // Gets the fence for the previous frame.
-    // Must be called on the main thread.
-    FenceWithFenceTime previousFrameFence();
+    bool wouldPresentEarly(TimePoint frameTime, Period) const REQUIRES(kMainThreadContext);
 
-    // Whether the previous frame has not yet been presented to the display.
-    // If graceTimeMs is positive, this method waits for at most the provided
-    // grace period before reporting if the frame missed.
-    // Must be called on the main thread.
-    bool previousFramePending(int graceTimeMs = 0);
+    const FenceTimePtr& getPreviousPresentFence(TimePoint frameTime, Period) const
+            REQUIRES(kMainThreadContext);
 
-    // Returns the previous time that the frame was presented. If the frame has
-    // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
-    // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
-    // Must be called on the main thread.
-    nsecs_t previousFramePresentTime();
+    // Blocks the thread waiting for up to graceTimeMs in case the fence is about to signal.
+    static bool isFencePending(const FenceTimePtr&, int graceTimeMs);
 
     // Calculates the expected present time for this frame. For negative offsets, performs a
     // correction using the predicted vsync for the next frame instead.
-
-    nsecs_t calculateExpectedPresentTime(DisplayStatInfo) const;
+    TimePoint calculateExpectedPresentTime(TimePoint frameTime) const;
 
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
+    sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
             REQUIRES(mStateLock) {
-        const sp<IBinder> nullToken;
-        return mPhysicalDisplayTokens.get(displayId).value_or(std::cref(nullToken));
+        return mPhysicalDisplays.get(displayId)
+                .transform([](const display::PhysicalDisplay& display) { return display.token(); })
+                .or_else([] { return std::optional<sp<display::DisplayToken>>(nullptr); })
+                .value();
     }
 
     std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked(
-            const sp<IBinder>& displayToken) const REQUIRES(mStateLock) {
-        for (const auto& [id, token] : mPhysicalDisplayTokens) {
-            if (token == displayToken) {
-                return id;
-            }
-        }
-        return {};
-    }
+            const sp<display::DisplayToken>&) const REQUIRES(mStateLock);
 
     // Returns the first display connected at boot.
     //
@@ -1085,15 +1051,18 @@
     VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
     void releaseVirtualDisplay(VirtualDisplayId);
 
-    void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) REQUIRES(mStateLock);
+    void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
+                                      const DisplayDevice& activeDisplay)
+            REQUIRES(mStateLock, kMainThreadContext);
 
-    void onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay);
+    void onActiveDisplaySizeChanged(const DisplayDevice&);
 
     /*
      * Debugging & dumpsys
      */
     void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers,
                        std::string& result) const REQUIRES(mStateLock);
+    void dumpHwcLayersMinidumpLocked(std::string& result) const REQUIRES(mStateLock);
 
     void appendSfConfigString(std::string& result) const;
     void listLayersLocked(std::string& result) const;
@@ -1101,10 +1070,11 @@
     void clearStatsLocked(const DumpArgs& args, std::string& result);
     void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
     void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
-    void logFrameStats() REQUIRES(kMainThreadContext);
+    void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
 
-    void dumpVSync(std::string& result) const REQUIRES(mStateLock);
-    void dumpStaticScreenStats(std::string& result) const;
+    void dumpScheduler(std::string& result) const REQUIRES(mStateLock);
+    void dumpEvents(std::string& result) const REQUIRES(mStateLock);
+    void dumpVsync(std::string& result) const REQUIRES(mStateLock);
 
     void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock);
     void dumpDisplays(std::string& result) const REQUIRES(mStateLock);
@@ -1115,7 +1085,9 @@
     LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
     void dumpOffscreenLayersProto(LayersProto& layersProto,
                                   uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
-    void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
+    google::protobuf::RepeatedPtrField<DisplayProto> dumpDisplayProto() const;
+    void addToLayerTracing(bool visibleRegionDirty, int64_t time, int64_t vsyncId)
+            REQUIRES(kMainThreadContext);
 
     // Dumps state from HW Composer
     void dumpHwc(std::string& result) const;
@@ -1141,52 +1113,52 @@
     status_t CheckTransactCodeCredentials(uint32_t code);
 
     // Add transaction to the Transaction Queue
-    void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
-    void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
-    void signalSynchronousTransactions(const uint32_t flag);
 
     /*
      * Generic Layer Metadata
      */
     const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
 
-    /*
-     * Misc
-     */
-    std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
-
     static int calculateMaxAcquiredBufferCount(Fps refreshRate,
                                                std::chrono::nanoseconds presentLatency);
     int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const;
 
-    void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
-            REQUIRES(mStateLock);
-
-    bool isHdrLayer(Layer* layer) const;
+    bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
 
     ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
             REQUIRES(mStateLock);
+    void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
 
     sp<StartPropertySetThread> mStartPropertySetThread;
     surfaceflinger::Factory& mFactory;
     pid_t mPid;
     std::future<void> mRenderEnginePrimeCacheFuture;
 
-    // access must be protected by mStateLock
+    // mStateLock has conventions related to the current thread, because only
+    // the main thread should modify variables protected by mStateLock.
+    // - read access from a non-main thread must lock mStateLock, since the main
+    // thread may modify these variables.
+    // - write access from a non-main thread is not permitted.
+    // - read access from the main thread can use an ftl::FakeGuard, since other
+    // threads must not modify these variables.
+    // - write access from the main thread must lock mStateLock, since another
+    // thread may be reading these variables.
     mutable Mutex mStateLock;
     State mCurrentState{LayerVector::StateSet::Current};
     std::atomic<int32_t> mTransactionFlags = 0;
-    std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
-    bool mAnimTransactionPending = false;
     std::atomic<uint32_t> mUniqueTransactionId = 1;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
 
+    // Buffers that have been discarded by clients and need to be evicted from per-layer caches so
+    // the graphics memory can be immediately freed.
+    std::vector<uint64_t> mBufferIdsToUncache;
+
     // global color transform states
     Daltonizer mDaltonizer;
     float mGlobalSaturationFactor = 1.0f;
     mat4 mClientColorMatrix;
 
-    size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+    size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
     // If there are more GraphicBufferProducers tracked by SurfaceFlinger than
     // this threshold, then begin logging.
     size_t mGraphicBufferProducerListSizeLogThreshold =
@@ -1201,7 +1173,6 @@
 
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
-    bool mGpuToCpuSupported = false;
     bool mIsUserBuild = true;
 
     // Can only accessed from the main thread, these members
@@ -1209,9 +1180,8 @@
     State mDrawingState{LayerVector::StateSet::Drawing};
     bool mVisibleRegionsDirty = false;
 
-    // VisibleRegions dirty is already cleared by postComp, but we need to track it to prevent
-    // extra work in the HDR layer info listener.
-    bool mVisibleRegionsWereDirtyThisFrame = false;
+    bool mHdrLayerInfoChanged = false;
+
     // Used to ensure we omit a callback when HDR layer info listener is newly added but the
     // scene hasn't changed
     bool mAddingHDRLayerInfoListener = false;
@@ -1220,44 +1190,48 @@
     // Set during transaction application stage to track if the input info or children
     // for a layer has changed.
     // TODO: Also move visibleRegions over to a boolean system.
-    bool mInputInfoChanged = false;
+    bool mUpdateInputInfo = false;
     bool mSomeChildrenChanged;
-    bool mSomeDataspaceChanged = false;
     bool mForceTransactionDisplayChange = false;
 
-    bool mAnimCompositionPending = false;
+    // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
+    bool mLayerMetadataSnapshotNeeded = false;
 
+    // TODO(b/238781169) validate these on composition
     // Tracks layers that have pending frames which are candidates for being
     // latched.
     std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
+    std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
     // Tracks layers that need to update a display's dirty region.
     std::vector<sp<Layer>> mLayersPendingRefresh;
-    // size should be longest sf-duration / shortest vsync period and round up
-    std::array<FenceWithFenceTime, 5> mPreviousPresentFences; // currently consider 166hz.
-    // True if in the previous frame at least one layer was composed via the GPU.
-    bool mHadClientComposition = false;
-    // True if in the previous frame at least one layer was composed via HW Composer.
-    // Note that it is possible for a frame to be composed via both client and device
-    // composition, for example in the case of overlays.
-    bool mHadDeviceComposition = false;
-    // True if in the previous frame, the client composition was skipped by reusing the buffer
-    // used in a previous composition. This can happed if the client composition requests
-    // did not change.
-    bool mReusedClientComposition = false;
+    // 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
+    // visible region changes. Because this is a list of strong pointers, this will
+    // extend the life of the layer but this list is only updated in the main thread.
+    std::vector<sp<Layer>> mPreviouslyComposedLayers;
 
     BootStage mBootStage = BootStage::BOOTLOADER;
 
-    std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
+    struct HotplugEvent {
+        hal::HWDisplayId hwcDisplayId;
+        hal::Connection connection = hal::Connection::INVALID;
+    };
+
+    std::mutex mHotplugMutex;
+    std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
 
     // Displays are composited in `mDisplays` order. Internal displays are inserted at boot and
     // never removed, so take precedence over external and virtual displays.
     //
-    // The static capacities were chosen to exceed a typical number of physical/virtual displays.
-    //
     // May be read from any thread, but must only be written from the main thread.
-    ftl::SmallMap<wp<IBinder>, const sp<DisplayDevice>, 5> mDisplays GUARDED_BY(mStateLock);
-    ftl::SmallMap<PhysicalDisplayId, const sp<IBinder>, 3> mPhysicalDisplayTokens
-            GUARDED_BY(mStateLock);
+    display::DisplayMap<wp<IBinder>, const sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+
+    display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
+
+    // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+    // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
+    // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
+    std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
 
     struct {
         DisplayIdGenerator<GpuVirtualDisplayId> gpu;
@@ -1271,10 +1245,9 @@
     std::atomic_bool mForceFullDamage = false;
 
     bool mLayerCachingEnabled = false;
-    bool mPropagateBackpressureClientComposition = false;
-    sp<SurfaceInterceptor> mInterceptor;
+    bool mBackpressureGpuComposition = false;
 
-    LayerTracing mLayerTracing{*this};
+    LayerTracing mLayerTracing;
     bool mLayerTracingEnabled = false;
 
     std::optional<TransactionTracing> mTransactionTracing;
@@ -1284,19 +1257,16 @@
     const std::unique_ptr<FrameTracer> mFrameTracer;
     const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline;
 
+    VsyncId mLastCommittedVsyncId;
+
     // If blurs should be enabled on this device.
     bool mSupportsBlur = false;
-    // If blurs are considered expensive and should require high GPU frequency.
-    bool mBlursAreExpensive = false;
     std::atomic<uint32_t> mFrameMissedCount = 0;
     std::atomic<uint32_t> mHwcFrameMissedCount = 0;
     std::atomic<uint32_t> mGpuFrameMissedCount = 0;
 
     TransactionCallbackInvoker mTransactionCallbackInvoker;
 
-    // Thread-safe.
-    FrameTracker mAnimFrameTracker;
-
     // We maintain a pool of pre-generated texture names to hand out to avoid
     // layer creation needing to run on the main thread (which it would
     // otherwise need to do to access RenderEngine).
@@ -1304,18 +1274,6 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    mutable Mutex mQueueLock;
-    Condition mTransactionQueueCV;
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
-            mPendingTransactionQueues GUARDED_BY(mQueueLock);
-    std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
-    /*
-     * Feature prototyping
-     */
-
-    // Static screen stats
-    bool mHasPoweredOff = false;
-
     std::atomic<size_t> mNumLayers = 0;
 
     // to linkToDeath
@@ -1338,21 +1296,28 @@
     // This property can be used to force SurfaceFlinger to always pick a certain color mode.
     ui::ColorMode mForceColorMode = ui::ColorMode::NATIVE;
 
+    // Whether to enable wide color gamut (e.g. Display P3) for internal displays that support it.
+    // If false, wide color modes are filtered out for all internal displays.
+    bool mSupportsWideColor = false;
+
     ui::Dataspace mDefaultCompositionDataspace;
     ui::Dataspace mWideColorGamutCompositionDataspace;
     ui::Dataspace mColorSpaceAgnosticDataspace;
     float mDimmingRatio = -1.f;
 
-    SurfaceFlingerBE mBE;
+    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::atomic<int> mNumTrustedPresentationListeners = 0;
+
     std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
+
+    CompositionCoverageFlags mCompositionCoverage;
+
     // mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
     // any mutex.
     size_t mMaxRenderTargetSize{1};
 
     const std::string mHwcServiceName;
 
-    bool hasMockHwc() const { return mHwcServiceName == "mock"; }
-
     /*
      * Scheduler
      */
@@ -1363,15 +1328,17 @@
     // Stores phase offsets configured per refresh rate.
     std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
 
-    // Optional to defer construction until PhaseConfiguration is created.
-    sp<VsyncModulator> mVsyncModulator;
-
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+    scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
 
-    std::atomic<nsecs_t> mExpectedPresentTime = 0;
-    nsecs_t mScheduledPresentTime = 0;
-    hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
-    hal::Vsync mLastHWCVsyncState = hal::Vsync::DISABLE;
+    struct FenceWithFenceTime {
+        sp<Fence> fence = Fence::NO_FENCE;
+        FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+    };
+    std::array<FenceWithFenceTime, 2> mPreviousPresentFences;
+
+    TimePoint mScheduledPresentTime GUARDED_BY(kMainThreadContext);
+    TimePoint mExpectedPresentTime GUARDED_BY(kMainThreadContext);
 
     // below flags are set by main thread only
     bool mSetActiveModePending = false;
@@ -1391,7 +1358,7 @@
 
     std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor;
 
-    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
+    void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext);
 
     // Flag used to set override desired display mode from backdoor
     bool mDebugDisplayModeSetByBackdoor = false;
@@ -1406,34 +1373,48 @@
 
     std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
             GUARDED_BY(mStateLock);
+
     mutable std::mutex mCreatedLayersLock;
-    struct LayerCreatedState {
-        LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
-              : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
-        wp<Layer> layer;
-        // Indicates the initial parent of the created layer, only used for creating layer in
-        // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
-        wp<Layer> initialParent;
-        // Indicates whether the layer getting created should be added at root if there's no parent
-        // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
-        // be added offscreen.
-        bool addToRoot;
-    };
 
     // A temporay pool that store the created layers and will be added to current state in main
     // thread.
     std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
-    bool commitCreatedLayers();
-    void handleLayerCreatedLocked(const LayerCreatedState& state) REQUIRES(mStateLock);
+    bool commitCreatedLayers(VsyncId, std::vector<LayerCreatedState>& createdLayers);
+    void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock);
+
+    mutable std::mutex mMirrorDisplayLock;
+    struct MirrorDisplayState {
+        MirrorDisplayState(ui::LayerStack layerStack, sp<IBinder>& rootHandle,
+                           const sp<Client>& client)
+              : layerStack(layerStack), rootHandle(rootHandle), client(client) {}
+
+        ui::LayerStack layerStack;
+        sp<IBinder> rootHandle;
+        const sp<Client> client;
+    };
+    std::vector<MirrorDisplayState> mMirrorDisplays GUARDED_BY(mMirrorDisplayLock);
+    bool commitMirrorDisplays(VsyncId);
 
     std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
 
+    // Must only be accessed on the main thread.
+    // TODO (b/259407931): Remove.
+    static ui::Transform::RotationFlags sActiveDisplayRotationFlags;
+
     bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
         return hasDisplay(
                 [](const auto& display) { return display.isRefreshRateOverlayEnabled(); });
     }
-
-    wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
+    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
+            std::optional<ui::LayerStack> layerStack, uint32_t uid,
+            std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
+                    snapshotFilterFn);
+    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
+            std::optional<ui::LayerStack> layerStack, uint32_t uid,
+            std::unordered_set<uint32_t> excludeLayerIds);
+    std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots(
+            uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds,
+            bool childrenOnly, const std::optional<FloatRect>& optionalParentCrop);
 
     const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
 
@@ -1446,38 +1427,67 @@
 
     bool mPowerHintSessionEnabled;
 
-    struct {
-        bool late = false;
-        bool early = false;
-    } mPowerHintSessionMode;
+    bool mLayerLifecycleManagerEnabled = false;
+    bool mLegacyFrontEndEnabled = true;
 
-    nsecs_t mAnimationTransactionTimeout = s2ns(5);
+    frontend::LayerLifecycleManager mLayerLifecycleManager;
+    frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
+    frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
 
-    friend class SurfaceComposerAIDL;
+    std::vector<uint32_t> mDestroyedHandles;
+    std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
+    std::vector<LayerCreationArgs> mNewLayerArgs;
+    // These classes do not store any client state but help with managing transaction callbacks
+    // and stats.
+    std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
 
-    // Layers visible during the last commit. This set should only be used for testing set equality
-    // and membership. The pointers should not be dereferenced as it's possible the set contains
-    // pointers to freed layers.
-    std::unordered_set<Layer*> mVisibleLayers;
+    TransactionHandler mTransactionHandler;
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
+    bool mFrontEndDisplayInfosChanged = false;
+
+    // WindowInfo ids visible during the last commit.
+    std::unordered_set<int32_t> mVisibleWindowIds;
 };
 
 class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
 public:
-    SurfaceComposerAIDL(sp<SurfaceFlinger> sf) { mFlinger = sf; }
+    SurfaceComposerAIDL(sp<SurfaceFlinger> sf) : mFlinger(std::move(sf)) {}
 
+    binder::Status bootFinished() override;
+    binder::Status createDisplayEventConnection(
+            VsyncSource vsyncSource, EventRegistration eventRegistration,
+            const sp<IBinder>& layerHandle,
+            sp<gui::IDisplayEventConnection>* outConnection) override;
+    binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
     binder::Status createDisplay(const std::string& displayName, bool secure,
-                                 sp<IBinder>* outDisplay) override;
+                                 float requestedRefreshRate, sp<IBinder>* outDisplay) override;
     binder::Status destroyDisplay(const sp<IBinder>& display) override;
     binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
-    binder::Status getPrimaryPhysicalDisplayId(int64_t* outDisplayId) override;
     binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;
     binder::Status setPowerMode(const sp<IBinder>& display, int mode) override;
+    binder::Status getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) override;
     binder::Status getDisplayStats(const sp<IBinder>& display,
                                    gui::DisplayStatInfo* outStatInfo) override;
     binder::Status getDisplayState(const sp<IBinder>& display,
                                    gui::DisplayState* outState) override;
+    binder::Status getStaticDisplayInfo(int64_t displayId,
+                                        gui::StaticDisplayInfo* outInfo) override;
+    binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+                                               gui::DynamicDisplayInfo* outInfo) override;
+    binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+                                                  gui::DynamicDisplayInfo* outInfo) override;
+    binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
+                                             gui::DisplayPrimaries* outPrimaries) override;
+    binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
+    binder::Status setBootDisplayMode(const sp<IBinder>& display, int displayModeId) override;
     binder::Status clearBootDisplayMode(const sp<IBinder>& display) override;
     binder::Status getBootDisplayModeSupport(bool* outMode) override;
+    binder::Status getOverlaySupport(gui::OverlayProperties* outProperties) override;
+    binder::Status getHdrConversionCapabilities(
+            std::vector<gui::HdrConversionCapability>*) override;
+    binder::Status setHdrConversionStrategy(const gui::HdrConversionStrategy& hdrConversionStrategy,
+                                            int32_t*) override;
+    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 captureDisplay(const DisplayCaptureArgs&,
@@ -1485,8 +1495,47 @@
     binder::Status captureDisplayById(int64_t, const sp<IScreenCaptureListener>&) override;
     binder::Status captureLayers(const LayerCaptureArgs&,
                                  const sp<IScreenCaptureListener>&) override;
+
+    // TODO(b/239076119): Remove deprecated AIDL.
+    [[deprecated]] binder::Status clearAnimationFrameStats() override {
+        return binder::Status::ok();
+    }
+    [[deprecated]] binder::Status getAnimationFrameStats(gui::FrameStats*) override {
+        return binder::Status::ok();
+    }
+
+    binder::Status overrideHdrTypes(const sp<IBinder>& display,
+                                    const std::vector<int32_t>& hdrTypes) override;
+    binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
+    binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
+    binder::Status getColorManagement(bool* outGetColorManagement) override;
+    binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
+    binder::Status getDisplayedContentSamplingAttributes(
+            const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override;
+    binder::Status setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
+                                                    int8_t componentMask,
+                                                    int64_t maxFrames) override;
+    binder::Status getDisplayedContentSample(const sp<IBinder>& display, int64_t maxFrames,
+                                             int64_t timestamp,
+                                             gui::DisplayedFrameStats* outStats) override;
+    binder::Status getProtectedContentSupport(bool* outSupporte) override;
     binder::Status isWideColorDisplay(const sp<IBinder>& token,
                                       bool* outIsWideColorDisplay) override;
+    binder::Status addRegionSamplingListener(
+            const gui::ARect& samplingArea, const sp<IBinder>& stopLayerHandle,
+            const sp<gui::IRegionSamplingListener>& listener) override;
+    binder::Status removeRegionSamplingListener(
+            const sp<gui::IRegionSamplingListener>& listener) override;
+    binder::Status addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override;
+    binder::Status removeFpsListener(const sp<gui::IFpsListener>& listener) override;
+    binder::Status addTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) override;
+    binder::Status removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) override;
+    binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                              const gui::DisplayModeSpecs&) override;
+    binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                              gui::DisplayModeSpecs* outSpecs) override;
     binder::Status getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                                bool* outSupport) override;
     binder::Status setDisplayBrightness(const sp<IBinder>& displayToken,
@@ -1496,12 +1545,29 @@
     binder::Status removeHdrLayerInfoListener(
             const sp<IBinder>& displayToken,
             const sp<gui::IHdrLayerInfoListener>& listener) override;
+
     binder::Status notifyPowerBoost(int boostId) override;
+    binder::Status setGlobalShadowSettings(const gui::Color& ambientColor,
+                                           const gui::Color& spotColor, float lightPosY,
+                                           float lightPosZ, float lightRadius) override;
+    binder::Status getDisplayDecorationSupport(
+            const sp<IBinder>& displayToken,
+            std::optional<gui::DisplayDecorationSupport>* outSupport) override;
+    binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
+    binder::Status getGpuContextPriority(int32_t* outPriority) override;
+    binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
+    binder::Status addWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+    binder::Status removeWindowInfosListener(
+            const sp<gui::IWindowInfosListener>& windowInfosListener) override;
 
 private:
     static const constexpr bool kUsePermissionCache = true;
     status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
     status_t checkControlDisplayBrightnessPermission();
+    status_t checkReadFrameBufferPermission();
+    static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+                                              gui::DynamicDisplayInfo*& outInfo);
 
 private:
     sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index b81b445..7e6894d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -22,22 +22,16 @@
 #include <cutils/properties.h>
 #include <ui/GraphicBuffer.h>
 
-#include "BufferLayerConsumer.h"
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
-#include "ContainerLayer.h"
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
-#include "MonitoredProducer.h"
 #include "NativeWindowSurface.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerDefaultFactory.h"
 #include "SurfaceFlingerProperties.h"
-#include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
+#include "FrameTimeline/FrameTimeline.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -59,23 +53,19 @@
     }
 }
 
-sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
-    return new android::impl::SurfaceInterceptor();
-}
-
 sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
         bool timestampPropertyValue) {
-    return new StartPropertySetThread(timestampPropertyValue);
+    return sp<StartPropertySetThread>::make(timestampPropertyValue);
 }
 
 sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) {
-    return new DisplayDevice(creationArgs);
+    return sp<DisplayDevice>::make(creationArgs);
 }
 
 sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height,
                                                       PixelFormat format, uint32_t layerCount,
                                                       uint64_t usage, std::string requestorName) {
-    return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+    return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName);
 }
 
 void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
@@ -84,18 +74,6 @@
     BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
 }
 
-sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
-        const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
-        const wp<Layer>& layer) {
-    return new MonitoredProducer(producer, flinger, layer);
-}
-
-sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
-        const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
-        uint32_t textureName, Layer* layer) {
-    return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-}
-
 std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
         const sp<IGraphicBufferProducer>& producer) {
     return surfaceflinger::impl::createNativeWindowSurface(producer);
@@ -105,20 +83,16 @@
     return compositionengine::impl::createCompositionEngine();
 }
 
-sp<ContainerLayer> DefaultFactory::createContainerLayer(const LayerCreationArgs& args) {
-    return new ContainerLayer(args);
+sp<Layer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
-sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) {
-    return new BufferQueueLayer(args);
+sp<Layer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
+    return sp<Layer>::make(args);
 }
 
-sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
-    return new BufferStateLayer(args);
-}
-
-sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
-    return new EffectLayer(args);
+sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName) {
+    return sp<LayerFE>::make(layerName);
 }
 
 std::unique_ptr<FrameTracer> DefaultFactory::createFrameTracer() {
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 501629d..2c6de0e 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,6 @@
     std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) override;
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -38,19 +37,12 @@
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                            sp<IGraphicBufferConsumer>* outConsumer,
                            bool consumerIsSurfaceFlinger) override;
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
-                                                       const sp<SurfaceFlinger>&,
-                                                       const wp<Layer>&) override;
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
-                                                      renderengine::RenderEngine&, uint32_t tex,
-                                                      Layer*) override;
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) override;
     std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
-    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override;
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
-    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
+    sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
+    sp<LayerFE> createLayerFE(const std::string& layerName) override;
     std::unique_ptr<FrameTracer> createFrameTracer() override;
     std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 3997b04..7bd6cf6 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -26,7 +26,7 @@
 sp<SurfaceFlinger> createSurfaceFlinger() {
     static DefaultFactory factory;
 
-    return new SurfaceFlinger(factory);
+    return sp<SurfaceFlinger>::make(factory);
 }
 
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 6153e8e..f310c4a 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -30,25 +30,20 @@
 
 typedef int32_t PixelFormat;
 
-class BufferQueueLayer;
 class BufferLayerConsumer;
-class BufferStateLayer;
-class ContainerLayer;
 class DisplayDevice;
-class EffectLayer;
 class FrameTracer;
 class GraphicBuffer;
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
 class Layer;
+class LayerFE;
 class StartPropertySetThread;
 class SurfaceFlinger;
-class SurfaceInterceptor;
 class TimeStats;
 
 struct DisplayDeviceCreationArgs;
-struct LayerCreationArgs;
 
 namespace compositionengine {
 class CompositionEngine;
@@ -57,7 +52,6 @@
 namespace scheduler {
 class VsyncConfiguration;
 class VsyncController;
-class RefreshRateConfigs;
 } // namespace scheduler
 
 namespace frametimeline {
@@ -66,6 +60,7 @@
 
 namespace surfaceflinger {
 
+struct LayerCreationArgs;
 class NativeWindowSurface;
 
 // The interface that SurfaceFlinger uses to create all of the implementations
@@ -75,7 +70,6 @@
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
             Fps currentRefreshRate) = 0;
-    virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
@@ -86,22 +80,15 @@
     virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                    sp<IGraphicBufferConsumer>* outConsumer,
                                    bool consumerIsSurfaceFlinger) = 0;
-    virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
-                                                               const sp<SurfaceFlinger>&,
-                                                               const wp<Layer>&) = 0;
-    virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
-                                                              renderengine::RenderEngine&,
-                                                              uint32_t tex, Layer*) = 0;
 
     virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) = 0;
 
     virtual std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() = 0;
 
-    virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<LayerFE> createLayerFE(const std::string& layerName) = 0;
     virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
     virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
             std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
deleted file mode 100644
index bace6cc..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * Copyright 2016 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"
-#undef LOG_TAG
-#define LOG_TAG "SurfaceInterceptor"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "Layer.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
-#include <fstream>
-
-#include <android-base/file.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-// TODO(marissaw): add new layer state values to SurfaceInterceptor
-
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-namespace impl {
-
-void SurfaceInterceptor::addTransactionTraceListener(
-        const sp<gui::ITransactionTraceListener>& listener) {
-    sp<IBinder> asBinder = IInterface::asBinder(listener);
-
-    std::scoped_lock lock(mListenersMutex);
-
-    asBinder->linkToDeath(this);
-
-    listener->onToggled(mEnabled); // notifies of current state
-
-    mTraceToggledListeners.emplace(asBinder, listener);
-}
-
-void SurfaceInterceptor::binderDied(const wp<IBinder>& who) {
-    std::scoped_lock lock(mListenersMutex);
-    mTraceToggledListeners.erase(who);
-}
-
-void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers,
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    if (mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(true);
-        }
-    }
-    mEnabled = true;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    saveExistingDisplaysLocked(displays);
-    saveExistingSurfacesLocked(layers);
-}
-
-void SurfaceInterceptor::disable() {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    {
-        std::scoped_lock lock(mListenersMutex);
-        for (const auto& [_, listener] : mTraceToggledListeners) {
-            listener->onToggled(false);
-        }
-    }
-    mEnabled = false;
-    std::scoped_lock<std::mutex> protoGuard(mTraceMutex);
-    status_t err(writeProtoFileLocked());
-    ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
-    ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
-    mTrace.Clear();
-}
-
-bool SurfaceInterceptor::isEnabled() {
-    return mEnabled;
-}
-
-void SurfaceInterceptor::saveExistingDisplaysLocked(
-        const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays)
-{
-    // Caveat: The initial snapshot does not capture the power mode of the existing displays
-    ATRACE_CALL();
-    for (size_t i = 0 ; i < displays.size() ; i++) {
-        addDisplayCreationLocked(createTraceIncrementLocked(), displays[i]);
-        addInitialDisplayStateLocked(createTraceIncrementLocked(), displays[i]);
-    }
-}
-
-void SurfaceInterceptor::saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers) {
-    ATRACE_CALL();
-    for (const auto& l : layers) {
-        l->traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* layer) {
-            addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
-            addInitialSurfaceStateLocked(createTraceIncrementLocked(), layer);
-        });
-    }
-}
-
-void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    const uint32_t layerFlags = layer->getTransactionFlags();
-    transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous);
-    transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation);
-
-    const int32_t layerId(getLayerId(layer));
-    addPositionLocked(transaction, layerId, layer->mDrawingState.transform.tx(),
-                      layer->mDrawingState.transform.ty());
-    addDepthLocked(transaction, layerId, layer->mDrawingState.z);
-    addAlphaLocked(transaction, layerId, layer->mDrawingState.color.a);
-    addTransparentRegionLocked(transaction, layerId,
-                               layer->mDrawingState.activeTransparentRegion_legacy);
-    addLayerStackLocked(transaction, layerId, layer->mDrawingState.layerStack);
-    addCropLocked(transaction, layerId, layer->mDrawingState.crop);
-    addCornerRadiusLocked(transaction, layerId, layer->mDrawingState.cornerRadius);
-    addBackgroundBlurRadiusLocked(transaction, layerId, layer->mDrawingState.backgroundBlurRadius);
-    addBlurRegionsLocked(transaction, layerId, layer->mDrawingState.blurRegions);
-    addFlagsLocked(transaction, layerId, layer->mDrawingState.flags,
-                   layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
-                           layer_state_t::eLayerSecure);
-    addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mDrawingParent));
-    addRelativeParentLocked(transaction, layerId,
-                            getLayerIdFromWeakRef(layer->mDrawingState.zOrderRelativeOf),
-                            layer->mDrawingState.z);
-    addShadowRadiusLocked(transaction, layerId, layer->mDrawingState.shadowRadius);
-    addTrustedOverlayLocked(transaction, layerId, layer->mDrawingState.isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
-        const DisplayDeviceState& display)
-{
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(false);
-    transaction->set_animation(false);
-
-    addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
-    addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
-    addDisplayFlagsLocked(transaction, display.sequenceId, display.flags);
-    addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
-    addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
-                               display.layerStackSpaceRect, display.orientedDisplaySpaceRect);
-}
-
-status_t SurfaceInterceptor::writeProtoFileLocked() {
-    ATRACE_CALL();
-    std::string output;
-
-    if (!mTrace.IsInitialized()) {
-        return NOT_ENOUGH_DATA;
-    }
-    if (!mTrace.SerializeToString(&output)) {
-        return PERMISSION_DENIED;
-    }
-    if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
-        return PERMISSION_DENIED;
-    }
-
-    return NO_ERROR;
-}
-
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const {
-    sp<IBinder> handle = weakHandle.promote();
-    return Layer::fromHandle(handle).promote();
-}
-
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
-    return layer->sequence;
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
-    if (layer == nullptr) {
-        return -1;
-    }
-    auto strongLayer = layer.promote();
-    return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
-}
-
-int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const {
-    if (handle == nullptr) {
-        return -1;
-    }
-    const sp<const Layer> layer = Layer::fromHandle(handle).promote();
-    return layer == nullptr ? -1 : getLayerId(layer);
-}
-
-Increment* SurfaceInterceptor::createTraceIncrementLocked() {
-    Increment* increment(mTrace.add_increment());
-    increment->set_time_stamp(elapsedRealtimeNano());
-    return increment;
-}
-
-SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transaction,
-        int32_t layerId)
-{
-    SurfaceChange* change(transaction->add_surface_change());
-    change->set_id(layerId);
-    return change;
-}
-
-DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction,
-        int32_t sequenceId)
-{
-    DisplayChange* dispChange(transaction->add_display_change());
-    dispChange->set_id(sequenceId);
-    return dispChange;
-}
-
-void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& rect) {
-    protoRect->set_left(rect.left);
-    protoRect->set_top(rect.top);
-    protoRect->set_right(rect.right);
-    protoRect->set_bottom(rect.bottom);
-}
-
-void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid,
-                                                    int32_t uid) {
-    Origin* origin(transaction->mutable_origin());
-    origin->set_pid(pid);
-    origin->set_uid(uid);
-}
-
-void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId,
-        float x, float y)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    PositionChange* posChange(change->mutable_position());
-    posChange->set_x(x);
-    posChange->set_y(y);
-}
-
-void SurfaceInterceptor::addDepthLocked(Transaction* transaction, int32_t layerId,
-        uint32_t z)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerChange* depthChange(change->mutable_layer());
-    depthChange->set_layer(z);
-}
-
-void SurfaceInterceptor::addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w,
-        uint32_t h)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    SizeChange* sizeChange(change->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addAlphaLocked(Transaction* transaction, int32_t layerId,
-        float alpha)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    AlphaChange* alphaChange(change->mutable_alpha());
-    alphaChange->set_alpha(alpha);
-}
-
-void SurfaceInterceptor::addMatrixLocked(Transaction* transaction, int32_t layerId,
-        const layer_state_t::matrix22_t& matrix)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    MatrixChange* matrixChange(change->mutable_matrix());
-    matrixChange->set_dsdx(matrix.dsdx);
-    matrixChange->set_dtdx(matrix.dtdx);
-    matrixChange->set_dsdy(matrix.dsdy);
-    matrixChange->set_dtdy(matrix.dtdy);
-}
-
-void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction,
-        int32_t layerId, const Region& transRegion)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TransparentRegionHintChange* transparentChange(change->mutable_transparent_region_hint());
-
-    for (const auto& rect : transRegion) {
-        Rectangle* protoRect(transparentChange->add_region());
-        setProtoRectLocked(protoRect, rect);
-    }
-}
-
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
-                                        uint8_t mask) {
-    // There can be multiple flags changed
-    if (mask & layer_state_t::eLayerHidden) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        HiddenFlagChange* flagChange(change->mutable_hidden_flag());
-        flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
-    }
-    if (mask & layer_state_t::eLayerOpaque) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
-        flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
-    }
-    if (mask & layer_state_t::eLayerSecure) {
-        SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-        SecureFlagChange* flagChange(change->mutable_secure_flag());
-        flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
-    }
-}
-
-void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
-                                             ui::LayerStack layerStack) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    LayerStackChange* layerStackChange(change->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
-        const Rect& rect)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CropChange* cropChange(change->mutable_crop());
-    Rectangle* protoRect(cropChange->mutable_rectangle());
-    setProtoRectLocked(protoRect, rect);
-}
-
-void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
-                                       float cornerRadius)
-{
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
-    cornerRadiusChange->set_corner_radius(cornerRadius);
-}
-
-void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
-                                                       int32_t backgroundBlurRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
-    blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
-}
-
-void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                                              const std::vector<BlurRegion>& blurRegions) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
-    for (const auto blurRegion : blurRegions) {
-        const auto blurRegionChange = blurRegionsChange->add_blur_regions();
-        blurRegionChange->set_blur_radius(blurRegion.blurRadius);
-        blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
-        blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
-        blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
-        blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
-        blurRegionChange->set_alpha(blurRegion.alpha);
-        blurRegionChange->set_left(blurRegion.left);
-        blurRegionChange->set_top(blurRegion.top);
-        blurRegionChange->set_right(blurRegion.right);
-        blurRegionChange->set_bottom(blurRegion.bottom);
-    }
-}
-
-void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
-                                           int32_t parentId) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ReparentChange* overrideChange(change->mutable_reparent());
-    overrideChange->set_parent_id(parentId);
-}
-
-void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
-                                                 int32_t parentId, int z) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    RelativeParentChange* overrideChange(change->mutable_relative_parent());
-    overrideChange->set_relative_parent_id(parentId);
-    overrideChange->set_z(z);
-}
-
-void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
-                                               float shadowRadius) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
-    overrideChange->set_radius(shadowRadius);
-}
-
-void SurfaceInterceptor::addTrustedOverlayLocked(Transaction* transaction, int32_t layerId,
-                                                 bool isTrustedOverlay) {
-    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
-    TrustedOverlayChange* overrideChange(change->mutable_trusted_overlay());
-    overrideChange->set_is_trusted_overlay(isTrustedOverlay);
-}
-
-void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
-        const layer_state_t& state)
-{
-    const sp<const Layer> layer(getLayer(state.surface));
-    if (layer == nullptr) {
-        ALOGE("An existing layer could not be retrieved with the surface "
-                "from the layer_state_t surface in the update transaction");
-        return;
-    }
-
-    const int32_t layerId(getLayerId(layer));
-
-    if (state.what & layer_state_t::ePositionChanged) {
-        addPositionLocked(transaction, layerId, state.x, state.y);
-    }
-    if (state.what & layer_state_t::eLayerChanged) {
-        addDepthLocked(transaction, layerId, state.z);
-    }
-    if (state.what & layer_state_t::eSizeChanged) {
-        addSizeLocked(transaction, layerId, state.w, state.h);
-    }
-    if (state.what & layer_state_t::eAlphaChanged) {
-        addAlphaLocked(transaction, layerId, state.alpha);
-    }
-    if (state.what & layer_state_t::eMatrixChanged) {
-        addMatrixLocked(transaction, layerId, state.matrix);
-    }
-    if (state.what & layer_state_t::eTransparentRegionChanged) {
-        addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
-    }
-    if (state.what & layer_state_t::eFlagsChanged) {
-        addFlagsLocked(transaction, layerId, state.flags, state.mask);
-    }
-    if (state.what & layer_state_t::eLayerStackChanged) {
-        addLayerStackLocked(transaction, layerId, state.layerStack);
-    }
-    if (state.what & layer_state_t::eCropChanged) {
-        addCropLocked(transaction, layerId, state.crop);
-    }
-    if (state.what & layer_state_t::eCornerRadiusChanged) {
-        addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
-    }
-    if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
-        addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
-    }
-    if (state.what & layer_state_t::eBlurRegionsChanged) {
-        addBlurRegionsLocked(transaction, layerId, state.blurRegions);
-    }
-    if (state.what & layer_state_t::eReparent) {
-        auto parentHandle = (state.parentSurfaceControlForChild)
-                ? state.parentSurfaceControlForChild->getHandle()
-                : nullptr;
-        addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle));
-    }
-    if (state.what & layer_state_t::eRelativeLayerChanged) {
-        addRelativeParentLocked(transaction, layerId,
-                                getLayerIdFromHandle(
-                                        state.relativeLayerSurfaceControl->getHandle()),
-                                state.z);
-    }
-    if (state.what & layer_state_t::eShadowRadiusChanged) {
-        addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
-    }
-    if (state.what & layer_state_t::eTrustedOverlayChanged) {
-        addTrustedOverlayLocked(transaction, layerId, state.isTrustedOverlay);
-    }
-    if (state.what & layer_state_t::eStretchChanged) {
-        ALOGW("SurfaceInterceptor not implemented for eStretchChanged");
-    }
-}
-
-void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
-        const DisplayState& state, int32_t sequenceId)
-{
-    if (state.what & DisplayState::eSurfaceChanged) {
-        addDisplaySurfaceLocked(transaction, sequenceId, state.surface);
-    }
-    if (state.what & DisplayState::eLayerStackChanged) {
-        addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack);
-    }
-    if (state.what & DisplayState::eFlagsChanged) {
-        addDisplayFlagsLocked(transaction, sequenceId, state.flags);
-    }
-    if (state.what & DisplayState::eDisplaySizeChanged) {
-        addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
-    }
-    if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
-                                   state.layerStackSpaceRect, state.orientedDisplaySpaceRect);
-    }
-}
-
-void SurfaceInterceptor::addTransactionLocked(
-        Increment* increment, const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid,
-        int originUid, uint64_t transactionId) {
-    Transaction* transaction(increment->mutable_transaction());
-    transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous);
-    transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation);
-    setTransactionOriginLocked(transaction, originPid, originUid);
-    transaction->set_id(transactionId);
-    for (const auto& compState: stateUpdates) {
-        addSurfaceChangesLocked(transaction, compState.state);
-    }
-    for (const auto& disp: changedDisplays) {
-        ssize_t dpyIdx = displays.indexOfKey(disp.token);
-        if (dpyIdx >= 0) {
-            const DisplayDeviceState& dispState(displays.valueAt(dpyIdx));
-            addDisplayChangesLocked(transaction, disp, dispState.sequenceId);
-        }
-    }
-}
-
-void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceCreation* creation(increment->mutable_surface_creation());
-    creation->set_id(getLayerId(layer));
-    creation->set_name(layer->getName());
-    creation->set_w(layer->mDrawingState.active_legacy.w);
-    creation->set_h(layer->mDrawingState.active_legacy.h);
-}
-
-void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment,
-        const sp<const Layer>& layer)
-{
-    SurfaceDeletion* deletion(increment->mutable_surface_deletion());
-    deletion->set_id(getLayerId(layer));
-}
-
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
-        uint32_t width, uint32_t height, uint64_t frameNumber)
-{
-    BufferUpdate* update(increment->mutable_buffer_update());
-    update->set_id(layerId);
-    update->set_w(width);
-    update->set_h(height);
-    update->set_frame_number(frameNumber);
-}
-
-void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp) {
-    VSyncEvent* event(increment->mutable_vsync_event());
-    event->set_when(timestamp);
-}
-
-void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
-        const sp<const IGraphicBufferProducer>& surface)
-{
-    if (surface == nullptr) {
-        return;
-    }
-    uint64_t bufferQueueId = 0;
-    status_t err(surface->getUniqueId(&bufferQueueId));
-    if (err == NO_ERROR) {
-        DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-        DispSurfaceChange* surfaceChange(dispChange->mutable_surface());
-        surfaceChange->set_buffer_queue_id(bufferQueueId);
-        surfaceChange->set_buffer_queue_name(surface->getConsumerName().c_str());
-    }
-    else {
-        ALOGE("invalid graphic buffer producer received while tracing a display change (%s)",
-                strerror(-err));
-    }
-}
-
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
-                                                    ui::LayerStack layerStack) {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
-    layerStackChange->set_layer_stack(layerStack.id);
-}
-
-void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
-                                               uint32_t flags) {
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    DisplayFlagsChange* flagsChange(dispChange->mutable_flags());
-    flagsChange->set_flags(flags);
-}
-
-void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId,
-        uint32_t w, uint32_t h)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    SizeChange* sizeChange(dispChange->mutable_size());
-    sizeChange->set_w(w);
-    sizeChange->set_h(h);
-}
-
-void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction,
-        int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame)
-{
-    DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
-    ProjectionChange* projectionChange(dispChange->mutable_projection());
-    projectionChange->set_orientation(orientation);
-    Rectangle* viewportRect(projectionChange->mutable_viewport());
-    setProtoRectLocked(viewportRect, viewport);
-    Rectangle* frameRect(projectionChange->mutable_frame());
-    setProtoRectLocked(frameRect, frame);
-}
-
-void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment,
-        const DisplayDeviceState& info)
-{
-    DisplayCreation* creation(increment->mutable_display_creation());
-    creation->set_id(info.sequenceId);
-    creation->set_name(info.displayName);
-    creation->set_is_secure(info.isSecure);
-    if (info.physical) {
-        creation->set_display_id(info.physical->id.value);
-    }
-}
-
-void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) {
-    DisplayDeletion* deletion(increment->mutable_display_deletion());
-    deletion->set_id(sequenceId);
-}
-
-void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId,
-        int32_t mode)
-{
-    PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update());
-    powerModeUpdate->set_id(sequenceId);
-    powerModeUpdate->set_mode(mode);
-}
-
-void SurfaceInterceptor::saveTransaction(
-        const Vector<ComposerState>& stateUpdates,
-        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-        const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid,
-        uint64_t transactionId) {
-    if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays,
-                         flags, originPid, originUid, transactionId);
-}
-
-void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceCreationLocked(createTraceIncrementLocked(), layer);
-}
-
-void SurfaceInterceptor::saveSurfaceDeletion(const sp<const Layer>& layer) {
-    if (!mEnabled || layer == nullptr) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
-}
-
-/**
- * Here we pass the layer by ID instead of by sp<> since this is called without
- * holding the state-lock from a Binder thread. If we required the caller
- * to pass 'this' by sp<> the temporary sp<> constructed could end up
- * being the last reference and we might accidentally destroy the Layer
- * from this binder thread.
- */
-void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
-        uint32_t height, uint64_t frameNumber)
-{
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
-}
-
-void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
-    if (!mEnabled) {
-        return;
-    }
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addVSyncUpdateLocked(createTraceIncrementLocked(), timestamp);
-}
-
-void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayCreationLocked(createTraceIncrementLocked(), info);
-}
-
-void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId);
-}
-
-void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) {
-    if (!mEnabled) {
-        return;
-    }
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode);
-}
-
-} // namespace impl
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
deleted file mode 100644
index 970c3e5..0000000
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-#ifndef ANDROID_SURFACEINTERCEPTOR_H
-#define ANDROID_SURFACEINTERCEPTOR_H
-
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-
-#include <mutex>
-
-#include <binder/IBinder.h>
-
-#include <gui/LayerState.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/SortedVector.h>
-#include <utils/StrongPointer.h>
-#include <utils/Vector.h>
-
-#include "DisplayDevice.h"
-
-namespace android {
-
-class BufferItem;
-class Layer;
-class SurfaceFlinger;
-struct ComposerState;
-struct DisplayDeviceState;
-struct DisplayState;
-struct layer_state_t;
-using Transaction = surfaceflinger::Transaction;
-using Trace = surfaceflinger::Trace;
-using Rectangle = surfaceflinger::Rectangle;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Increment = surfaceflinger::Increment;
-using DisplayChange = surfaceflinger::DisplayChange;
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-class SurfaceInterceptor : public IBinder::DeathRecipient {
-public:
-    virtual ~SurfaceInterceptor();
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    virtual void enable(const SortedVector<sp<Layer>>& layers,
-                        const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) = 0;
-    virtual void disable() = 0;
-    virtual bool isEnabled() = 0;
-
-    virtual void addTransactionTraceListener(
-            const sp<gui::ITransactionTraceListener>& listener) = 0;
-    virtual void binderDied(const wp<IBinder>& who) = 0;
-
-    // Intercept display and surface transactions
-    virtual void saveTransaction(
-            const Vector<ComposerState>& stateUpdates,
-            const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-            const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-            int originUid, uint64_t transactionId) = 0;
-
-    // Intercept surface data
-    virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
-    virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
-    virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                                  uint64_t frameNumber) = 0;
-
-    // Intercept display data
-    virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0;
-    virtual void saveDisplayDeletion(int32_t sequenceId) = 0;
-    virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0;
-    virtual void saveVSyncEvent(nsecs_t timestamp) = 0;
-};
-
-namespace impl {
-
-/*
- * SurfaceInterceptor intercepts and stores incoming streams of window
- * properties on SurfaceFlinger.
- */
-class SurfaceInterceptor final : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor() = default;
-    ~SurfaceInterceptor() override = default;
-
-    // Both vectors are used to capture the current state of SF as the initial snapshot in the trace
-    void enable(const SortedVector<sp<Layer>>& layers,
-                const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays) override;
-    void disable() override;
-    bool isEnabled() override;
-
-    void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override;
-    void binderDied(const wp<IBinder>& who) override;
-
-    // Intercept display and surface transactions
-    void saveTransaction(const Vector<ComposerState>& stateUpdates,
-                         const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                         const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid,
-                         int originUid, uint64_t transactionId) override;
-
-    // Intercept surface data
-    void saveSurfaceCreation(const sp<const Layer>& layer) override;
-    void saveSurfaceDeletion(const sp<const Layer>& layer) override;
-    void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
-                          uint64_t frameNumber) override;
-
-    // Intercept display data
-    void saveDisplayCreation(const DisplayDeviceState& info) override;
-    void saveDisplayDeletion(int32_t sequenceId) override;
-    void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override;
-    void saveVSyncEvent(nsecs_t timestamp) override;
-
-private:
-    // The creation increments of Surfaces and Displays do not contain enough information to capture
-    // the initial state of each object, so a transaction with all of the missing properties is
-    // performed at the initial snapshot for each display and surface.
-    void saveExistingDisplaysLocked(
-            const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays);
-    void saveExistingSurfacesLocked(const SortedVector<sp<Layer>>& layers);
-    void addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer);
-    void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
-
-    status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const;
-    int32_t getLayerId(const sp<const Layer>& layer) const;
-    int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
-    int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const;
-
-    Increment* createTraceIncrementLocked();
-    void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
-    void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
-    void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
-            uint32_t height, uint64_t frameNumber);
-    void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
-    void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
-    void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId);
-    void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode);
-
-    // Add surface transactions to the trace
-    SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId);
-    void setProtoRectLocked(Rectangle* protoRect, const Rect& rect);
-    void addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y);
-    void addDepthLocked(Transaction* transaction, int32_t layerId, uint32_t z);
-    void addSizeLocked(Transaction* transaction, int32_t layerId, uint32_t w, uint32_t h);
-    void addAlphaLocked(Transaction* transaction, int32_t layerId, float alpha);
-    void addMatrixLocked(Transaction* transaction, int32_t layerId,
-            const layer_state_t::matrix22_t& matrix);
-    void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
-            const Region& transRegion);
-    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
-    void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
-    void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
-    void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
-    void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
-                                       int32_t backgroundBlurRadius);
-    void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
-                              const std::vector<BlurRegion>& effectRegions);
-    void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
-    void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
-                              const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
-                              const Vector<DisplayState>& changedDisplays,
-                              uint32_t transactionFlags, int originPid, int originUid,
-                              uint64_t transactionId);
-    void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
-    void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
-                                 int z);
-    void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
-    void addTrustedOverlayLocked(Transaction* transaction, int32_t layerId, bool isTrustedOverlay);
-
-    // Add display transactions to the trace
-    DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
-    void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
-            const sp<const IGraphicBufferProducer>& surface);
-    void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
-    void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
-    void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
-            uint32_t h);
-    void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId,
-            int32_t orientation, const Rect& viewport, const Rect& frame);
-    void addDisplayChangesLocked(Transaction* transaction,
-            const DisplayState& state, int32_t sequenceId);
-
-    // Add transaction origin to trace
-    void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid);
-
-    bool mEnabled {false};
-    std::string mOutputFileName {DEFAULT_FILENAME};
-    std::mutex mTraceMutex {};
-    Trace mTrace {};
-    std::mutex mListenersMutex;
-    std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners
-            GUARDED_BY(mListenersMutex);
-};
-
-} // namespace impl
-
-} // namespace android
-
-#endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 57752b7..155a275 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -7,6 +7,14 @@
       "name": "libcompositionengine_test"
     },
     {
+      "name": "libgui_test",
+      "options": [
+        {
+          "native-test-flag": "--gtest_filter=\"InputSurfacesTest*:MultiDisplayTests*\""
+        }
+      ]
+    },
+    {
       "name": "libscheduler_test"
     }
   ],
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index e5a9dd4..630cef1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -27,6 +27,7 @@
 
 #include <algorithm>
 #include <chrono>
+#include <cmath>
 #include <unordered_map>
 
 #include "TimeStats.h"
@@ -68,6 +69,8 @@
             return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
         case GameMode::Battery:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
+        case GameMode::Custom:
+            return SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM;
         default:
             return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
     }
@@ -88,7 +91,7 @@
 }
 } // namespace
 
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     if (mTimeStats.statsStartLegacy == 0) {
@@ -136,10 +139,11 @@
     // Always clear data.
     clearGlobalLocked();
 
-    return atomList.SerializeToString(pulledData);
+    pulledData->resize(atomList.ByteSizeLong());
+    return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
 }
 
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -177,6 +181,12 @@
             *atom->mutable_present_to_present() =
                     histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
         }
+        const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta");
+        if (present2PresentDeltaHist != layer->deltas.cend()) {
+            *atom->mutable_present_to_present_delta() =
+                    histogramToProto(present2PresentDeltaHist->second.hist,
+                                     mMaxPulledHistogramBuckets);
+        }
         const auto& post2presentHist = layer->deltas.find("post2present");
         if (post2presentHist != layer->deltas.cend()) {
             *atom->mutable_post_to_present() =
@@ -227,7 +237,8 @@
     // Always clear data.
     clearLayersLocked();
 
-    return atomList.SerializeToString(pulledData);
+    pulledData->resize(atomList.ByteSizeLong());
+    return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
 }
 
 TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -243,7 +254,7 @@
     }
 }
 
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
     bool success = false;
     if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
         success = populateGlobalAtom(pulledData);
@@ -448,6 +459,7 @@
 
     LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
+    std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     const int32_t refreshRateBucket =
             clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -525,6 +537,12 @@
             ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
+            if (prevPresentToPresentMs) {
+                const int32_t presentToPresentDeltaMs =
+                        std::abs(presentToPresentMs - *prevPresentToPresentMs);
+                timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs);
+            }
+            prevPresentToPresentMs = presentToPresentMs;
         }
         prevTimeRecord = timeRecords[0];
         timeRecords.pop_front();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 7a159b8..5f58657 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -34,6 +34,8 @@
 
 #include <scheduler/Fps.h>
 
+using android::gui::GameMode;
+using android::gui::LayerMetadata;
 using namespace android::surfaceflinger;
 
 namespace android {
@@ -45,7 +47,7 @@
     virtual ~TimeStats() = default;
 
     // Process a pull request from statsd.
-    virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+    virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
 
     virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
     virtual bool isEnabled() = 0;
@@ -217,6 +219,7 @@
         uint32_t lateAcquireFrames = 0;
         uint32_t badDesiredPresentFrames = 0;
         TimeRecord prevTimeRecord;
+        std::optional<int32_t> prevPresentToPresentMs;
         std::deque<TimeRecord> timeRecords;
     };
 
@@ -242,7 +245,7 @@
     TimeStats(std::optional<size_t> maxPulledLayers,
               std::optional<size_t> maxPulledHistogramBuckets);
 
-    bool onPullAtom(const int atomId, std::string* pulledData) override;
+    bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
     std::string miniDump() override;
@@ -290,8 +293,8 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    bool populateGlobalAtom(std::string* pulledData);
-    bool populateLayerAtom(std::string* pulledData);
+    bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+    bool populateLayerAtom(std::vector<uint8_t>* pulledData);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
                                             std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index e45757d..8615947 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -173,6 +173,7 @@
          GAME_MODE_STANDARD = 2;
          GAME_MODE_PERFORMANCE = 3;
          GAME_MODE_BATTERY = 4;
+         GAME_MODE_CUSTOM = 5;
     }
 
     // Game mode that the layer was running at. Used to track user engagement
@@ -288,7 +289,11 @@
     // Introduced in Android 12.
     optional FrameTimingHistogram app_deadline_misses = 25;
 
-    // Next ID: 27
+    // Variability histogram of present_to_present timings.
+    // Introduced in Android 14.
+    optional FrameTimingHistogram present_to_present_delta = 27;
+
+    // Next ID: 28
 }
 
 /**
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 237ae8d..60aa810 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -24,6 +24,9 @@
 #include <unordered_map>
 #include <vector>
 
+using android::gui::GameMode;
+using android::gui::LayerMetadata;
+
 namespace android {
 namespace surfaceflinger {
 
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 558b3be..1adc3a5 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -24,16 +24,24 @@
 #include <cutils/compiler.h>
 #include <utils/Trace.h>
 
-namespace std {
-template <class Rep, class Period>
-bool signbit(std::chrono::duration<Rep, Period> v) {
-    return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
-}
-} // namespace std
-
 namespace android {
 
 namespace {
+template <class Rep, class Period>
+bool signbit(std::chrono::duration<Rep, Period> v) {
+    return std::signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count());
+}
+
+template <typename Enum, typename std::enable_if<std::is_enum<Enum>::value>::type* = nullptr>
+bool signbit(Enum e) {
+    return std::signbit(static_cast<typename std::underlying_type<Enum>::type>(e));
+}
+
+template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
+bool signbit(T t) {
+    return std::signbit(t);
+}
+
 template <typename T>
 int64_t to_int64(T v) {
     return int64_t(v);
@@ -49,14 +57,12 @@
 class TracedOrdinal {
 public:
     static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) ||
-                          std::is_same<std::chrono::nanoseconds, T>(),
+                          std::is_same<std::chrono::nanoseconds, T>() || std::is_enum<T>(),
                   "Type is not supported. Please test it with systrace before adding "
                   "it to the list.");
 
     TracedOrdinal(std::string name, T initialValue)
-          : mName(std::move(name)),
-            mHasGoneNegative(std::signbit(initialValue)),
-            mData(initialValue) {
+          : mName(std::move(name)), mHasGoneNegative(signbit(initialValue)), mData(initialValue) {
         trace();
     }
 
@@ -66,7 +72,7 @@
 
     TracedOrdinal& operator=(T other) {
         mData = other;
-        mHasGoneNegative = mHasGoneNegative || std::signbit(mData);
+        mHasGoneNegative = mHasGoneNegative || signbit(mData);
         trace();
         return *this;
     }
@@ -81,7 +87,7 @@
             mNameNegative = mName + "Negative";
         }
 
-        if (!std::signbit(mData)) {
+        if (!signbit(mData)) {
             ATRACE_INT64(mName.c_str(), to_int64(mData));
             if (mHasGoneNegative) {
                 ATRACE_INT64(mNameNegative.c_str(), 0);
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 49554c7..ecdeabe 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "LayerTracing"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <filesystem>
+
 #include <SurfaceFlinger.h>
 #include <android-base/stringprintf.h>
 #include <log/log.h>
@@ -29,9 +31,8 @@
 
 namespace android {
 
-LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
-    mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
-}
+LayerTracing::LayerTracing()
+      : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {}
 
 LayerTracing::~LayerTracing() = default;
 
@@ -45,30 +46,39 @@
     return true;
 }
 
-bool LayerTracing::disable(std::string filename) {
+bool LayerTracing::disable(std::string filename, bool writeToFile) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return false;
     }
     mEnabled = false;
-    LayersTraceFileProto fileProto = createTraceFileProto();
-    mBuffer->writeToFile(fileProto, filename);
+    if (writeToFile) {
+        LayersTraceFileProto fileProto = createTraceFileProto();
+        mBuffer->writeToFile(fileProto, filename);
+    }
     mBuffer->reset();
     return true;
 }
 
+void LayerTracing::appendToStream(std::ofstream& out) {
+    std::scoped_lock lock(mTraceLock);
+    LayersTraceFileProto fileProto = createTraceFileProto();
+    mBuffer->appendToStream(fileProto, out);
+    mBuffer->reset();
+}
+
 bool LayerTracing::isEnabled() const {
     std::scoped_lock lock(mTraceLock);
     return mEnabled;
 }
 
-status_t LayerTracing::writeToFile() {
+status_t LayerTracing::writeToFile(std::string filename) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return STATUS_OK;
     }
     LayersTraceFileProto fileProto = createTraceFileProto();
-    return mBuffer->writeToFile(fileProto, FILE_NAME);
+    return mBuffer->writeToFile(fileProto, filename);
 }
 
 void LayerTracing::setTraceFlags(uint32_t flags) {
@@ -84,11 +94,17 @@
 bool LayerTracing::flagIsSet(uint32_t flags) const {
     return (mFlags & flags) == flags;
 }
+uint32_t LayerTracing::getFlags() const {
+    return mFlags;
+}
 
-LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+LayersTraceFileProto LayerTracing::createTraceFileProto() {
     LayersTraceFileProto fileProto;
     fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
                                LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+    auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
+                                                   systemTime(SYSTEM_TIME_MONOTONIC));
+    fileProto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
     return fileProto;
 }
 
@@ -98,7 +114,9 @@
     mBuffer->dump(result);
 }
 
-void LayerTracing::notify(bool visibleRegionDirty, int64_t time) {
+void LayerTracing::notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId,
+                          LayersProto* layers, std::string hwcDump,
+                          google::protobuf::RepeatedPtrField<DisplayProto>* displays) {
     std::scoped_lock lock(mTraceLock);
     if (!mEnabled) {
         return;
@@ -113,22 +131,16 @@
     entry.set_elapsed_realtime_nanos(time);
     const char* where = visibleRegionDirty ? "visibleRegionsDirty" : "bufferLatched";
     entry.set_where(where);
-    LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
-
-    if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
-        mFlinger.dumpOffscreenLayersProto(layers);
-    }
-    entry.mutable_layers()->Swap(&layers);
+    entry.mutable_layers()->Swap(layers);
 
     if (flagIsSet(LayerTracing::TRACE_HWC)) {
-        std::string hwcDump;
-        mFlinger.dumpHwc(hwcDump);
         entry.set_hwc_blob(hwcDump);
     }
     if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
         entry.set_excludes_composition_state(true);
     }
-    mFlinger.dumpDisplayProto(entry);
+    entry.mutable_displays()->Swap(displays);
+    entry.set_vsync_id(vsyncId);
     mBuffer->emplace(std::move(entry));
 }
 
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 88a19ec..40b0fbe 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -40,14 +40,16 @@
  */
 class LayerTracing {
 public:
-    LayerTracing(SurfaceFlinger& flinger);
+    LayerTracing();
     ~LayerTracing();
     bool enable();
-    bool disable(std::string filename = FILE_NAME);
+    bool disable(std::string filename = FILE_NAME, bool writeToFile = true);
+    void appendToStream(std::ofstream& out);
     bool isEnabled() const;
-    status_t writeToFile();
-    LayersTraceFileProto createTraceFileProto() const;
-    void notify(bool visibleRegionDirty, int64_t time);
+    status_t writeToFile(std::string filename = FILE_NAME);
+    static LayersTraceFileProto createTraceFileProto();
+    void notify(bool visibleRegionDirty, int64_t time, int64_t vsyncId, LayersProto* layers,
+                std::string hwcDump, google::protobuf::RepeatedPtrField<DisplayProto>* displays);
 
     enum : uint32_t {
         TRACE_INPUT = 1 << 1,
@@ -55,17 +57,17 @@
         TRACE_EXTRA = 1 << 3,
         TRACE_HWC = 1 << 4,
         TRACE_BUFFERS = 1 << 5,
+        TRACE_VIRTUAL_DISPLAYS = 1 << 6,
         TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
     };
     void setTraceFlags(uint32_t flags);
     bool flagIsSet(uint32_t flags) const;
+    uint32_t getFlags() const;
     void setBufferSize(size_t bufferSizeInBytes);
     void dump(std::string&) const;
 
 private:
     static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
-    SurfaceFlinger& mFlinger;
     uint32_t mFlags = TRACE_INPUT;
     mutable std::mutex mTraceLock;
     bool mEnabled GUARDED_BY(mTraceLock) = false;
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index 7e38c55..b41c65b 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -24,6 +24,7 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 #include <chrono>
+#include <fstream>
 #include <queue>
 
 namespace android {
@@ -73,6 +74,19 @@
         return NO_ERROR;
     }
 
+    status_t appendToStream(FileProto& fileProto, std::ofstream& out) {
+        ATRACE_CALL();
+        writeToProto(fileProto);
+        std::string output;
+        if (!fileProto.SerializeToString(&output)) {
+            ALOGE("Could not serialize proto.");
+            return UNKNOWN_ERROR;
+        }
+
+        out << output;
+        return NO_ERROR;
+    }
+
     std::vector<std::string> emplace(std::string&& serializedProto) {
         std::vector<std::string> replacedEntries;
         size_t protoSize = static_cast<size_t>(serializedProto.size());
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index a73eccf..0694180 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -15,13 +15,42 @@
  */
 
 #include <gui/SurfaceComposerClient.h>
+#include <ui/Fence.h>
 #include <ui/Rect.h>
 
+#include "FrontEnd/LayerCreationArgs.h"
 #include "LayerProtoHelper.h"
 #include "TransactionProtoParser.h"
+#include "TransactionState.h"
+#include "gui/LayerState.h"
 
 namespace android::surfaceflinger {
 
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mEmptyBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(uint32_t width, uint32_t height, uint64_t id, PixelFormat pixelFormat,
+                        uint64_t usage)
+          : mWidth(width), mHeight(height), mId(id), mPixelFormat(pixelFormat), mUsage(usage) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    void remapBuffer() override {}
+    ~FakeExternalTexture() = default;
+};
+
 proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t) {
     proto::TransactionState proto;
     proto.set_pid(t.originPid);
@@ -33,51 +62,40 @@
 
     proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
     for (auto& layerState : t.states) {
-        proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state)));
+        proto.mutable_layer_changes()->Add(std::move(toProto(layerState)));
     }
 
     proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
     for (auto& displayState : t.displays) {
         proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
     }
+
+    proto.mutable_merged_transaction_ids()->Reserve(
+            static_cast<int32_t>(t.mergedTransactionIds.size()));
+    for (auto& mergedTransactionId : t.mergedTransactionIds) {
+        proto.mutable_merged_transaction_ids()->Add(mergedTransactionId);
+    }
+
     return proto;
 }
 
 proto::TransactionState TransactionProtoParser::toProto(
-        const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+        const std::map<uint32_t /* layerId */, TracingLayerState>& states) {
     proto::TransactionState proto;
     proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(states.size()));
     for (auto& [layerId, state] : states) {
         proto::LayerState layerProto = toProto(state);
-        if (layerProto.has_buffer_data()) {
-            proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
-            bufferProto->set_buffer_id(state.bufferId);
-            bufferProto->set_width(state.bufferWidth);
-            bufferProto->set_height(state.bufferHeight);
-            bufferProto->set_pixel_format(
-                    static_cast<proto::LayerState_BufferData_PixelFormat>(state.pixelFormat));
-            bufferProto->set_usage(state.bufferUsage);
-        }
         layerProto.set_has_sideband_stream(state.hasSidebandStream);
-        layerProto.set_layer_id(state.layerId);
-        layerProto.set_parent_id(state.parentId);
-        layerProto.set_relative_parent_id(state.relativeParentId);
-        if (layerProto.has_window_info_handle()) {
-            layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
-        }
         proto.mutable_layer_changes()->Add(std::move(layerProto));
     }
     return proto;
 }
 
-proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) {
+proto::LayerState TransactionProtoParser::toProto(
+        const ResolvedComposerState& resolvedComposerState) {
     proto::LayerState proto;
-    if (layer.surface) {
-        proto.set_layer_id(mMapper->getLayerId(layer.surface));
-    } else {
-        proto.set_layer_id(layer.layerId);
-    }
-
+    auto& layer = resolvedComposerState.state;
+    proto.set_layer_id(resolvedComposerState.layerId);
     proto.set_what(layer.what);
 
     if (layer.what & layer_state_t::ePositionChanged) {
@@ -87,10 +105,7 @@
     if (layer.what & layer_state_t::eLayerChanged) {
         proto.set_z(layer.z);
     }
-    if (layer.what & layer_state_t::eSizeChanged) {
-        proto.set_w(layer.w);
-        proto.set_h(layer.h);
-    }
+
     if (layer.what & layer_state_t::eLayerStackChanged) {
         proto.set_layer_stack(layer.layerStack.id);
     }
@@ -113,7 +128,7 @@
     }
 
     if (layer.what & layer_state_t::eAlphaChanged) {
-        proto.set_alpha(layer.alpha);
+        proto.set_alpha(layer.color.a);
     }
 
     if (layer.what & layer_state_t::eColorChanged) {
@@ -125,8 +140,8 @@
     if (layer.what & layer_state_t::eTransparentRegionChanged) {
         LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
     }
-    if (layer.what & layer_state_t::eTransformChanged) {
-        proto.set_transform(layer.transform);
+    if (layer.what & layer_state_t::eBufferTransformChanged) {
+        proto.set_transform(layer.bufferTransform);
     }
     if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
         proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
@@ -136,27 +151,13 @@
     }
     if (layer.what & layer_state_t::eBufferChanged) {
         proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
-        if (layer.bufferData->hasBuffer()) {
-            bufferProto->set_buffer_id(layer.bufferData->getId());
-            bufferProto->set_width(layer.bufferData->getWidth());
-            bufferProto->set_height(layer.bufferData->getHeight());
+        if (resolvedComposerState.externalTexture) {
+            bufferProto->set_buffer_id(resolvedComposerState.externalTexture->getId());
+            bufferProto->set_width(resolvedComposerState.externalTexture->getWidth());
+            bufferProto->set_height(resolvedComposerState.externalTexture->getHeight());
             bufferProto->set_pixel_format(static_cast<proto::LayerState_BufferData_PixelFormat>(
-                    layer.bufferData->getPixelFormat()));
-            bufferProto->set_usage(layer.bufferData->getUsage());
-        } else {
-            uint64_t bufferId;
-            uint32_t width;
-            uint32_t height;
-            int32_t pixelFormat;
-            uint64_t usage;
-            mMapper->getGraphicBufferPropertiesFromCache(layer.bufferData->cachedBuffer, &bufferId,
-                                                         &width, &height, &pixelFormat, &usage);
-            bufferProto->set_buffer_id(bufferId);
-            bufferProto->set_width(width);
-            bufferProto->set_height(height);
-            bufferProto->set_pixel_format(
-                    static_cast<proto::LayerState_BufferData_PixelFormat>(pixelFormat));
-            bufferProto->set_usage(usage);
+                    resolvedComposerState.externalTexture->getPixelFormat()));
+            bufferProto->set_usage(resolvedComposerState.externalTexture->getUsage());
         }
         bufferProto->set_frame_number(layer.bufferData->frameNumber);
         bufferProto->set_flags(layer.bufferData->flags.get());
@@ -180,16 +181,10 @@
     }
 
     if (layer.what & layer_state_t::eReparent) {
-        int64_t layerId = layer.parentSurfaceControlForChild
-                ? mMapper->getLayerId(layer.parentSurfaceControlForChild->getHandle())
-                : -1;
-        proto.set_parent_id(layerId);
+        proto.set_parent_id(resolvedComposerState.parentId);
     }
     if (layer.what & layer_state_t::eRelativeLayerChanged) {
-        int64_t layerId = layer.relativeLayerSurfaceControl
-                ? mMapper->getLayerId(layer.relativeLayerSurfaceControl->getHandle())
-                : -1;
-        proto.set_relative_parent_id(layerId);
+        proto.set_relative_parent_id(resolvedComposerState.relativeParentId);
         proto.set_z(layer.z);
     }
 
@@ -208,7 +203,7 @@
             windowInfoProto->set_has_wallpaper(inputInfo->inputConfig.test(
                     gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER));
             windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
-            proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+            proto::Transform* transformProto = windowInfoProto->mutable_transform();
             transformProto->set_dsdx(inputInfo->transform.dsdx());
             transformProto->set_dtdx(inputInfo->transform.dtdx());
             transformProto->set_dtdy(inputInfo->transform.dtdy());
@@ -217,17 +212,16 @@
             transformProto->set_ty(inputInfo->transform.ty());
             windowInfoProto->set_replace_touchable_region_with_crop(
                     inputInfo->replaceTouchableRegionWithCrop);
-            windowInfoProto->set_crop_layer_id(
-                    mMapper->getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+            windowInfoProto->set_crop_layer_id(resolvedComposerState.touchCropId);
         }
     }
     if (layer.what & layer_state_t::eBackgroundColorChanged) {
-        proto.set_bg_color_alpha(layer.bgColorAlpha);
+        proto.set_bg_color_alpha(layer.bgColor.a);
         proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
         proto::LayerState_Color3* colorProto = proto.mutable_color();
-        colorProto->set_r(layer.color.r);
-        colorProto->set_g(layer.color.g);
-        colorProto->set_b(layer.color.b);
+        colorProto->set_r(layer.bgColor.r);
+        colorProto->set_g(layer.bgColor.g);
+        colorProto->set_b(layer.bgColor.b);
     }
     if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
         proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
@@ -290,13 +284,15 @@
     return proto;
 }
 
-proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+proto::LayerCreationArgs TransactionProtoParser::toProto(const LayerCreationArgs& args) {
     proto::LayerCreationArgs proto;
-    proto.set_layer_id(args.layerId);
+    proto.set_layer_id(args.sequence);
     proto.set_name(args.name);
     proto.set_flags(args.flags);
     proto.set_parent_id(args.parentId);
-    proto.set_mirror_from_id(args.mirrorFromId);
+    proto.set_mirror_from_id(args.layerIdToMirror);
+    proto.set_add_to_root(args.addToRoot);
+    proto.set_layer_stack_to_mirror(args.layerStackToMirror.id);
     return proto;
 }
 
@@ -312,10 +308,10 @@
     int32_t layerCount = proto.layer_changes_size();
     t.states.reserve(static_cast<size_t>(layerCount));
     for (int i = 0; i < layerCount; i++) {
-        ComposerState s;
+        ResolvedComposerState s;
         s.state.what = 0;
-        fromProto(proto.layer_changes(i), s.state);
-        t.states.add(s);
+        fromProto(proto.layer_changes(i), s);
+        t.states.emplace_back(s);
     }
 
     int32_t displayCount = proto.display_changes_size();
@@ -327,46 +323,47 @@
 }
 
 void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
-                                       TracingLayerCreationArgs& outArgs) {
-    outArgs.layerId = proto.layer_id();
+                                       LayerCreationArgs& outArgs) {
+    outArgs.sequence = proto.layer_id();
+
     outArgs.name = proto.name();
     outArgs.flags = proto.flags();
     outArgs.parentId = proto.parent_id();
-    outArgs.mirrorFromId = proto.mirror_from_id();
+    outArgs.layerIdToMirror = proto.mirror_from_id();
+    outArgs.addToRoot = proto.add_to_root();
+    outArgs.layerStackToMirror.id = proto.layer_stack_to_mirror();
 }
 
 void TransactionProtoParser::mergeFromProto(const proto::LayerState& proto,
                                             TracingLayerState& outState) {
-    layer_state_t state;
-    fromProto(proto, state);
-    outState.merge(state);
+    ResolvedComposerState resolvedComposerState;
+    fromProto(proto, resolvedComposerState);
+    layer_state_t& state = resolvedComposerState.state;
+    outState.state.merge(state);
+    outState.layerId = resolvedComposerState.layerId;
 
     if (state.what & layer_state_t::eReparent) {
-        outState.parentId = static_cast<int32_t>(proto.parent_id());
+        outState.parentId = resolvedComposerState.parentId;
     }
     if (state.what & layer_state_t::eRelativeLayerChanged) {
-        outState.relativeParentId = static_cast<int32_t>(proto.relative_parent_id());
+        outState.relativeParentId = resolvedComposerState.relativeParentId;
     }
     if (state.what & layer_state_t::eInputInfoChanged) {
-        outState.inputCropId = static_cast<int32_t>(proto.window_info_handle().crop_layer_id());
+        outState.touchCropId = resolvedComposerState.touchCropId;
     }
     if (state.what & layer_state_t::eBufferChanged) {
-        const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
-        outState.bufferId = bufferProto.buffer_id();
-        outState.bufferWidth = bufferProto.width();
-        outState.bufferHeight = bufferProto.height();
-        outState.pixelFormat = bufferProto.pixel_format();
-        outState.bufferUsage = bufferProto.usage();
+        outState.externalTexture = resolvedComposerState.externalTexture;
     }
     if (state.what & layer_state_t::eSidebandStreamChanged) {
         outState.hasSidebandStream = proto.has_sideband_stream();
     }
 }
 
-void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_state_t& layer) {
-    layer.layerId = (int32_t)proto.layer_id();
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+                                       ResolvedComposerState& resolvedComposerState) {
+    auto& layer = resolvedComposerState.state;
+    resolvedComposerState.layerId = proto.layer_id();
     layer.what |= proto.what();
-    layer.surface = mMapper->getLayerHandle(layer.layerId);
 
     if (proto.what() & layer_state_t::ePositionChanged) {
         layer.x = proto.x();
@@ -375,10 +372,6 @@
     if (proto.what() & layer_state_t::eLayerChanged) {
         layer.z = proto.z();
     }
-    if (proto.what() & layer_state_t::eSizeChanged) {
-        layer.w = proto.w();
-        layer.h = proto.h();
-    }
     if (proto.what() & layer_state_t::eLayerStackChanged) {
         layer.layerStack.id = proto.layer_stack();
     }
@@ -401,7 +394,7 @@
     }
 
     if (proto.what() & layer_state_t::eAlphaChanged) {
-        layer.alpha = proto.alpha();
+        layer.color.a = proto.alpha();
     }
 
     if (proto.what() & layer_state_t::eColorChanged) {
@@ -413,8 +406,8 @@
     if (proto.what() & layer_state_t::eTransparentRegionChanged) {
         LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
     }
-    if (proto.what() & layer_state_t::eTransformChanged) {
-        layer.transform = proto.transform();
+    if (proto.what() & layer_state_t::eBufferTransformChanged) {
+        layer.bufferTransform = proto.transform();
     }
     if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
         layer.transformToDisplayInverse = proto.transform_to_display_inverse();
@@ -425,9 +418,15 @@
     if (proto.what() & layer_state_t::eBufferChanged) {
         const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
         layer.bufferData =
-                std::move(mMapper->getGraphicData(bufferProto.buffer_id(), bufferProto.width(),
-                                                  bufferProto.height(), bufferProto.pixel_format(),
-                                                  bufferProto.usage()));
+                std::make_shared<fake::BufferData>(bufferProto.buffer_id(), bufferProto.width(),
+                                                   bufferProto.height(), bufferProto.pixel_format(),
+                                                   bufferProto.usage());
+        resolvedComposerState.externalTexture =
+                std::make_shared<FakeExternalTexture>(layer.bufferData->getWidth(),
+                                                      layer.bufferData->getHeight(),
+                                                      layer.bufferData->getId(),
+                                                      layer.bufferData->getPixelFormat(),
+                                                      layer.bufferData->getUsage());
         layer.bufferData->frameNumber = bufferProto.frame_number();
         layer.bufferData->flags = ftl::Flags<BufferData::BufferDataChange>(bufferProto.flags());
         layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
@@ -451,26 +450,10 @@
     }
 
     if (proto.what() & layer_state_t::eReparent) {
-        int64_t layerId = proto.parent_id();
-        if (layerId == -1) {
-            layer.parentSurfaceControlForChild = nullptr;
-        } else {
-            layer.parentSurfaceControlForChild =
-                    new SurfaceControl(SurfaceComposerClient::getDefault(),
-                                       mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                       nullptr, static_cast<int32_t>(layerId));
-        }
+        resolvedComposerState.parentId = proto.parent_id();
     }
     if (proto.what() & layer_state_t::eRelativeLayerChanged) {
-        int64_t layerId = proto.relative_parent_id();
-        if (layerId == -1) {
-            layer.relativeLayerSurfaceControl = nullptr;
-        } else {
-            layer.relativeLayerSurfaceControl =
-                    new SurfaceControl(SurfaceComposerClient::getDefault(),
-                                       mMapper->getLayerHandle(static_cast<int32_t>(layerId)),
-                                       nullptr, static_cast<int32_t>(layerId));
-        }
+        resolvedComposerState.relativeParentId = proto.relative_parent_id();
         layer.z = proto.z();
     }
 
@@ -490,24 +473,23 @@
         inputInfo.setInputConfig(gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER,
                                  windowInfoProto.has_wallpaper());
         inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
-        const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+        const proto::Transform& transformProto = windowInfoProto.transform();
         inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
                                 transformProto.dsdy());
         inputInfo.transform.set(transformProto.tx(), transformProto.ty());
         inputInfo.replaceTouchableRegionWithCrop =
                 windowInfoProto.replace_touchable_region_with_crop();
-        int64_t layerId = windowInfoProto.crop_layer_id();
-        inputInfo.touchableRegionCropHandle =
-                mMapper->getLayerHandle(static_cast<int32_t>(layerId));
+        resolvedComposerState.touchCropId = windowInfoProto.crop_layer_id();
+
         layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
     }
     if (proto.what() & layer_state_t::eBackgroundColorChanged) {
-        layer.bgColorAlpha = proto.bg_color_alpha();
+        layer.bgColor.a = proto.bg_color_alpha();
         layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
         const proto::LayerState_Color3& colorProto = proto.color();
-        layer.color.r = colorProto.r();
-        layer.color.g = colorProto.g();
-        layer.color.b = colorProto.b();
+        layer.bgColor.r = colorProto.r();
+        layer.bgColor.g = colorProto.g();
+        layer.bgColor.b = colorProto.b();
     }
     if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
         layer.colorSpaceAgnostic = proto.color_space_agnostic();
@@ -569,4 +551,62 @@
     return display;
 }
 
+void asProto(proto::Transform* proto, const ui::Transform& transform) {
+    proto->set_dsdx(transform.dsdx());
+    proto->set_dtdx(transform.dtdx());
+    proto->set_dtdy(transform.dtdy());
+    proto->set_dsdy(transform.dsdy());
+    proto->set_tx(transform.tx());
+    proto->set_ty(transform.ty());
+}
+
+proto::DisplayInfo TransactionProtoParser::toProto(const frontend::DisplayInfo& displayInfo,
+                                                   uint32_t layerStack) {
+    proto::DisplayInfo proto;
+    proto.set_layer_stack(layerStack);
+    proto.set_display_id(displayInfo.info.displayId);
+    proto.set_logical_width(displayInfo.info.logicalWidth);
+    proto.set_logical_height(displayInfo.info.logicalHeight);
+    asProto(proto.mutable_transform_inverse(), displayInfo.info.transform);
+    asProto(proto.mutable_transform(), displayInfo.transform);
+    proto.set_receives_input(displayInfo.receivesInput);
+    proto.set_is_secure(displayInfo.isSecure);
+    proto.set_is_primary(displayInfo.isPrimary);
+    proto.set_is_virtual(displayInfo.isVirtual);
+    proto.set_rotation_flags((int)displayInfo.rotationFlags);
+    proto.set_transform_hint((int)displayInfo.transformHint);
+    return proto;
+}
+
+void fromProto2(ui::Transform& outTransform, const proto::Transform& proto) {
+    outTransform.set(proto.dsdx(), proto.dtdx(), proto.dtdy(), proto.dsdy());
+    outTransform.set(proto.tx(), proto.ty());
+}
+
+frontend::DisplayInfo TransactionProtoParser::fromProto(const proto::DisplayInfo& proto) {
+    frontend::DisplayInfo displayInfo;
+    displayInfo.info.displayId = proto.display_id();
+    displayInfo.info.logicalWidth = proto.logical_width();
+    displayInfo.info.logicalHeight = proto.logical_height();
+    fromProto2(displayInfo.info.transform, proto.transform_inverse());
+    fromProto2(displayInfo.transform, proto.transform());
+    displayInfo.receivesInput = proto.receives_input();
+    displayInfo.isSecure = proto.is_secure();
+    displayInfo.isPrimary = proto.is_primary();
+    displayInfo.isVirtual = proto.is_virtual();
+    displayInfo.rotationFlags = (ui::Transform::RotationFlags)proto.rotation_flags();
+    displayInfo.transformHint = (ui::Transform::RotationFlags)proto.transform_hint();
+    return displayInfo;
+}
+
+void TransactionProtoParser::fromProto(
+        const google::protobuf::RepeatedPtrField<proto::DisplayInfo>& proto,
+        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& outDisplayInfos) {
+    outDisplayInfos.clear();
+    for (const proto::DisplayInfo& displayInfo : proto) {
+        outDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(displayInfo.layer_stack()),
+                                           fromProto(displayInfo));
+    }
+}
+
 } // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index 872a901..d6c98e1 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -15,61 +15,20 @@
  */
 #pragma once
 
+#include <gui/fake/BufferData.h>
 #include <layerproto/TransactionProto.h>
 #include <utils/RefBase.h>
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
 
+#include "FrontEnd/LayerCreationArgs.h"
 #include "TransactionState.h"
 
 namespace android::surfaceflinger {
 
-struct TracingLayerCreationArgs {
-    int32_t layerId;
-    std::string name;
-    uint32_t flags = 0;
-    int32_t parentId = -1;
-    int32_t mirrorFromId = -1;
-};
-
-struct TracingLayerState : layer_state_t {
-    uint64_t bufferId;
-    uint32_t bufferHeight;
-    uint32_t bufferWidth;
-    int32_t pixelFormat;
-    uint64_t bufferUsage;
+struct TracingLayerState : ResolvedComposerState {
     bool hasSidebandStream;
-    int32_t parentId;
-    int32_t relativeParentId;
-    int32_t inputCropId;
-    TracingLayerCreationArgs args;
-};
-
-// Class which exposes buffer properties from BufferData without holding on to the actual buffer
-// handle.
-class BufferDataStub : public BufferData {
-public:
-    BufferDataStub(uint64_t bufferId, uint32_t width, uint32_t height, int32_t pixelFormat,
-                   uint64_t outUsage)
-          : mBufferId(bufferId),
-            mWidth(width),
-            mHeight(height),
-            mPixelFormat(pixelFormat),
-            mOutUsage(outUsage) {}
-    bool hasBuffer() const override { return mBufferId != 0; }
-    bool hasSameBuffer(const BufferData& other) const override {
-        return getId() == other.getId() && frameNumber == other.frameNumber;
-    }
-    uint32_t getWidth() const override { return mWidth; }
-    uint32_t getHeight() const override { return mHeight; }
-    uint64_t getId() const override { return mBufferId; }
-    PixelFormat getPixelFormat() const override { return mPixelFormat; }
-    uint64_t getUsage() const override { return mOutUsage; }
-
-private:
-    uint64_t mBufferId;
-    uint32_t mWidth;
-    uint32_t mHeight;
-    int32_t mPixelFormat;
-    uint64_t mOutUsage;
+    LayerCreationArgs args;
 };
 
 class TransactionProtoParser {
@@ -79,40 +38,31 @@
     class FlingerDataMapper {
     public:
         virtual ~FlingerDataMapper() = default;
-        virtual sp<IBinder> getLayerHandle(int32_t /* layerId */) const { return nullptr; }
-        virtual int64_t getLayerId(const sp<IBinder>& /* layerHandle */) const { return -1; }
-        virtual int64_t getLayerId(BBinder* /* layerHandle */) const { return -1; }
         virtual sp<IBinder> getDisplayHandle(int32_t /* displayId */) const { return nullptr; }
         virtual int32_t getDisplayId(const sp<IBinder>& /* displayHandle */) const { return -1; }
-        virtual std::shared_ptr<BufferData> getGraphicData(uint64_t bufferId, uint32_t width,
-                                                           uint32_t height, int32_t pixelFormat,
-                                                           uint64_t usage) const {
-            return std::make_shared<BufferDataStub>(bufferId, width, height, pixelFormat, usage);
-        }
-        virtual void getGraphicBufferPropertiesFromCache(client_cache_t /* cachedBuffer */,
-                                                         uint64_t* /* outBufferId */,
-                                                         uint32_t* /* outWidth */,
-                                                         uint32_t* /* outHeight */,
-                                                         int32_t* /* outPixelFormat */,
-                                                         uint64_t* /* outUsage */) const {}
     };
 
     TransactionProtoParser(std::unique_ptr<FlingerDataMapper> provider)
           : mMapper(std::move(provider)) {}
 
     proto::TransactionState toProto(const TransactionState&);
-    proto::TransactionState toProto(const std::map<int32_t /* layerId */, TracingLayerState>&);
-    proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+    proto::TransactionState toProto(const std::map<uint32_t /* layerId */, TracingLayerState>&);
+    proto::LayerCreationArgs toProto(const LayerCreationArgs& args);
+    proto::LayerState toProto(const ResolvedComposerState&);
+    static proto::DisplayInfo toProto(const frontend::DisplayInfo&, uint32_t layerStack);
 
     TransactionState fromProto(const proto::TransactionState&);
     void mergeFromProto(const proto::LayerState&, TracingLayerState& outState);
-    void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+    void fromProto(const proto::LayerCreationArgs&, LayerCreationArgs& outArgs);
     std::unique_ptr<FlingerDataMapper> mMapper;
+    static frontend::DisplayInfo fromProto(const proto::DisplayInfo&);
+    static void fromProto(
+            const google::protobuf::RepeatedPtrField<proto::DisplayInfo>&,
+            display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& outDisplayInfos);
 
 private:
-    proto::LayerState toProto(const layer_state_t&);
     proto::DisplayState toProto(const DisplayState&);
-    void fromProto(const proto::LayerState&, layer_state_t& out);
+    void fromProto(const proto::LayerState&, ResolvedComposerState& out);
     DisplayState fromProto(const proto::DisplayState&);
 
 };
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 6381758..87a633f 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -23,75 +23,14 @@
 #include <utils/SystemClock.h>
 #include <utils/Trace.h>
 
-#include "ClientCache.h"
+#include "Client.h"
+#include "FrontEnd/LayerCreationArgs.h"
 #include "TransactionTracing.h"
-#include "renderengine/ExternalTexture.h"
 
 namespace android {
 
-// Keeps the binder address as the layer id so we can avoid holding the tracing lock in the
-// binder thread.
-class FlatDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
-    virtual int64_t getLayerId(const sp<IBinder>& layerHandle) const {
-        if (layerHandle == nullptr) {
-            return -1;
-        }
-
-        return reinterpret_cast<int64_t>(layerHandle->localBinder());
-    }
-
-    void getGraphicBufferPropertiesFromCache(client_cache_t cachedBuffer, uint64_t* outBufferId,
-                                             uint32_t* outWidth, uint32_t* outHeight,
-                                             int32_t* outPixelFormat,
-                                             uint64_t* outUsage) const override {
-        std::shared_ptr<renderengine::ExternalTexture> buffer =
-                ClientCache::getInstance().get(cachedBuffer);
-        if (!buffer || !buffer->getBuffer()) {
-            *outBufferId = 0;
-            *outWidth = 0;
-            *outHeight = 0;
-            *outPixelFormat = 0;
-            *outUsage = 0;
-            return;
-        }
-
-        *outBufferId = buffer->getId();
-        *outWidth = buffer->getWidth();
-        *outHeight = buffer->getHeight();
-        *outPixelFormat = buffer->getPixelFormat();
-        *outUsage = buffer->getUsage();
-        return;
-    }
-};
-
-class FlingerDataMapper : public FlatDataMapper {
-    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */>& mLayerHandles;
-
-public:
-    FlingerDataMapper(std::unordered_map<BBinder* /* handle */, int32_t /* id */>& layerHandles)
-          : mLayerHandles(layerHandles) {}
-
-    int64_t getLayerId(const sp<IBinder>& layerHandle) const override {
-        if (layerHandle == nullptr) {
-            return -1;
-        }
-        return getLayerId(layerHandle->localBinder());
-    }
-
-    int64_t getLayerId(BBinder* localBinder) const {
-        auto it = mLayerHandles.find(localBinder);
-        if (it == mLayerHandles.end()) {
-            ALOGW("Could not find layer handle %p", localBinder);
-            return -1;
-        }
-        return it->second;
-    }
-};
-
 TransactionTracing::TransactionTracing()
-      : mProtoParser(std::make_unique<FlingerDataMapper>(mLayerHandles)),
-        mLockfreeProtoParser(std::make_unique<FlatDataMapper>()) {
+      : mProtoParser(std::make_unique<TransactionProtoParser::FlingerDataMapper>()) {
     std::scoped_lock lock(mTraceLock);
 
     mBuffer.setSize(mBufferSizeInBytes);
@@ -134,68 +73,80 @@
     proto::TransactionTraceFile proto;
     proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
                            proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
+    auto timeOffsetNs = static_cast<std::uint64_t>(systemTime(SYSTEM_TIME_REALTIME) -
+                                                   systemTime(SYSTEM_TIME_MONOTONIC));
+    proto.set_real_to_elapsed_time_offset_nanos(timeOffsetNs);
+    proto.set_version(TRACING_VERSION);
     return proto;
 }
 
 void TransactionTracing::dump(std::string& result) const {
     std::scoped_lock lock(mTraceLock);
-    base::StringAppendF(&result,
-                        "  queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
-                        mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
-                        mStartingStates.size());
+    base::StringAppendF(&result, "  queued transactions=%zu created layers=%zu states=%zu\n",
+                        mQueuedTransactions.size(), mCreatedLayers.size(), mStartingStates.size());
     mBuffer.dump(result);
 }
 
 void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
-    proto::TransactionState* state =
-            new proto::TransactionState(mLockfreeProtoParser.toProto(transaction));
+    proto::TransactionState* state = new proto::TransactionState(mProtoParser.toProto(transaction));
     mTransactionQueue.push(state);
 }
 
-void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
-                                                  int64_t vsyncId) {
-    CommittedTransactions committedTransactions;
-    committedTransactions.vsyncId = vsyncId;
-    committedTransactions.timestamp = systemTime();
-    committedTransactions.transactionIds.reserve(transactions.size());
-    for (const auto& transaction : transactions) {
-        committedTransactions.transactionIds.emplace_back(transaction.id);
+void TransactionTracing::addCommittedTransactions(
+        int64_t vsyncId, nsecs_t commitTime, frontend::Update& newUpdate,
+        const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+        bool displayInfoChanged) {
+    CommittedUpdates update;
+    update.vsyncId = vsyncId;
+    update.timestamp = commitTime;
+    update.transactionIds.reserve(newUpdate.transactions.size());
+    for (const auto& transaction : newUpdate.transactions) {
+        update.transactionIds.emplace_back(transaction.id);
     }
-
-    mPendingTransactions.emplace_back(committedTransactions);
+    update.displayInfoChanged = displayInfoChanged;
+    if (displayInfoChanged) {
+        update.displayInfos = displayInfos;
+    }
+    update.createdLayers = std::move(newUpdate.layerCreationArgs);
+    newUpdate.layerCreationArgs.clear();
+    update.destroyedLayerHandles.reserve(newUpdate.destroyedHandles.size());
+    for (uint32_t handle : newUpdate.destroyedHandles) {
+        update.destroyedLayerHandles.push_back(handle);
+    }
+    mPendingUpdates.emplace_back(update);
     tryPushToTracingThread();
 }
 
 void TransactionTracing::loop() {
     while (true) {
-        std::vector<CommittedTransactions> committedTransactions;
-        std::vector<int32_t> removedLayers;
+        std::vector<CommittedUpdates> committedUpdates;
+        std::vector<uint32_t> destroyedLayers;
         {
             std::unique_lock<std::mutex> lock(mMainThreadLock);
             base::ScopedLockAssertion assumeLocked(mMainThreadLock);
             mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
-                return mDone || !mCommittedTransactions.empty();
+                return mDone || !mUpdates.empty();
             });
             if (mDone) {
-                mCommittedTransactions.clear();
-                mRemovedLayers.clear();
+                mUpdates.clear();
+                mDestroyedLayers.clear();
                 break;
             }
 
-            removedLayers = std::move(mRemovedLayers);
-            mRemovedLayers.clear();
-            committedTransactions = std::move(mCommittedTransactions);
-            mCommittedTransactions.clear();
+            destroyedLayers = std::move(mDestroyedLayers);
+            mDestroyedLayers.clear();
+            committedUpdates = std::move(mUpdates);
+            mUpdates.clear();
         } // unlock mMainThreadLock
 
-        if (!committedTransactions.empty() || !removedLayers.empty()) {
-            addEntry(committedTransactions, removedLayers);
+        if (!committedUpdates.empty() || !destroyedLayers.empty()) {
+            addEntry(committedUpdates, destroyedLayers);
         }
     }
 }
 
-void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
-                                  const std::vector<int32_t>& removedLayers) {
+void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates,
+                                  const std::vector<uint32_t>& destroyedLayers) {
     ATRACE_CALL();
     std::scoped_lock lock(mTraceLock);
     std::vector<std::string> removedEntries;
@@ -203,50 +154,27 @@
 
     while (auto incomingTransaction = mTransactionQueue.pop()) {
         auto transaction = *incomingTransaction;
-        int32_t layerCount = transaction.layer_changes_size();
-        for (int i = 0; i < layerCount; i++) {
-            auto layer = transaction.mutable_layer_changes(i);
-            layer->set_layer_id(
-                mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(layer->layer_id())));
-            if ((layer->what() & layer_state_t::eReparent) && layer->parent_id() != -1) {
-                layer->set_parent_id(
-                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                        layer->parent_id())));
-            }
-
-            if ((layer->what() & layer_state_t::eRelativeLayerChanged) &&
-                layer->relative_parent_id() != -1) {
-                layer->set_relative_parent_id(
-                    mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                        layer->relative_parent_id())));
-            }
-
-            if (layer->has_window_info_handle() &&
-                layer->window_info_handle().crop_layer_id() != -1) {
-                auto input = layer->mutable_window_info_handle();
-                input->set_crop_layer_id(
-                        mProtoParser.mMapper->getLayerId(reinterpret_cast<BBinder*>(
-                            input->crop_layer_id())));
-            }
-        }
         mQueuedTransactions[incomingTransaction->transaction_id()] = transaction;
         delete incomingTransaction;
     }
-    for (const CommittedTransactions& entry : committedTransactions) {
-        entryProto.set_elapsed_realtime_nanos(entry.timestamp);
-        entryProto.set_vsync_id(entry.vsyncId);
-        entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
-        for (auto& newLayer : mCreatedLayers) {
-            entryProto.mutable_added_layers()->Add(std::move(newLayer));
+    for (const CommittedUpdates& update : committedUpdates) {
+        entryProto.set_elapsed_realtime_nanos(update.timestamp);
+        entryProto.set_vsync_id(update.vsyncId);
+        entryProto.mutable_added_layers()->Reserve(
+                static_cast<int32_t>(update.createdLayers.size()));
+
+        for (const auto& args : update.createdLayers) {
+            entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
         }
-        entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
-        for (auto& removedLayer : removedLayers) {
-            entryProto.mutable_removed_layers()->Add(removedLayer);
+
+        entryProto.mutable_destroyed_layers()->Reserve(
+                static_cast<int32_t>(destroyedLayers.size()));
+        for (auto& destroyedLayer : destroyedLayers) {
+            entryProto.mutable_destroyed_layers()->Add(destroyedLayer);
         }
-        mCreatedLayers.clear();
         entryProto.mutable_transactions()->Reserve(
-                static_cast<int32_t>(entry.transactionIds.size()));
-        for (const uint64_t& id : entry.transactionIds) {
+                static_cast<int32_t>(update.transactionIds.size()));
+        for (const uint64_t& id : update.transactionIds) {
             auto it = mQueuedTransactions.find(id);
             if (it != mQueuedTransactions.end()) {
                 entryProto.mutable_transactions()->Add(std::move(it->second));
@@ -256,6 +184,22 @@
             }
         }
 
+        entryProto.mutable_destroyed_layer_handles()->Reserve(
+                static_cast<int32_t>(update.destroyedLayerHandles.size()));
+        for (auto layerId : update.destroyedLayerHandles) {
+            entryProto.mutable_destroyed_layer_handles()->Add(layerId);
+        }
+
+        entryProto.set_displays_changed(update.displayInfoChanged);
+        if (update.displayInfoChanged) {
+            entryProto.mutable_displays()->Reserve(
+                    static_cast<int32_t>(update.displayInfos.size()));
+            for (auto& [layerStack, displayInfo] : update.displayInfos) {
+                entryProto.mutable_displays()->Add(
+                        std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
+            }
+        }
+
         std::string serializedProto;
         entryProto.SerializeToString(&serializedProto);
         entryProto.Clear();
@@ -263,13 +207,6 @@
         removedEntries.reserve(removedEntries.size() + entries.size());
         removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
                               std::make_move_iterator(entries.end()));
-
-        entryProto.mutable_removed_layer_handles()->Reserve(
-                static_cast<int32_t>(mRemovedLayerHandles.size()));
-        for (auto& handle : mRemovedLayerHandles) {
-            entryProto.mutable_removed_layer_handles()->Add(handle);
-        }
-        mRemovedLayerHandles.clear();
     }
 
     proto::TransactionTraceEntry removedEntryProto;
@@ -282,7 +219,7 @@
 }
 
 void TransactionTracing::flush(int64_t vsyncId) {
-    while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+    while (!mPendingUpdates.empty() || !mPendingDestroyedLayers.empty()) {
         tryPushToTracingThread();
     }
     std::unique_lock<std::mutex> lock(mTraceLock);
@@ -296,56 +233,21 @@
     });
 }
 
-void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
-                                      uint32_t flags, int parentId) {
-    std::scoped_lock lock(mTraceLock);
-    TracingLayerCreationArgs args{layerId, name, flags, parentId, -1 /* mirrorFromId */};
-    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
-        ALOGW("Duplicate handles found. %p", layerHandle);
-    }
-    mLayerHandles[layerHandle] = layerId;
-    mCreatedLayers.push_back(mProtoParser.toProto(args));
-}
-
-void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
-                                            const std::string& name, int mirrorFromId) {
-    std::scoped_lock lock(mTraceLock);
-    TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
-    if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
-        ALOGW("Duplicate handles found. %p", layerHandle);
-    }
-    mLayerHandles[layerHandle] = layerId;
-    mCreatedLayers.emplace_back(mProtoParser.toProto(args));
-}
-
 void TransactionTracing::onLayerRemoved(int32_t layerId) {
-    mPendingRemovedLayers.emplace_back(layerId);
+    mPendingDestroyedLayers.emplace_back(layerId);
     tryPushToTracingThread();
 }
 
-void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
-    std::scoped_lock lock(mTraceLock);
-    auto it = mLayerHandles.find(layerHandle);
-    if (it == mLayerHandles.end()) {
-        ALOGW("handle not found. %p", layerHandle);
-        return;
-    }
-
-    mRemovedLayerHandles.push_back(it->second);
-    mLayerHandles.erase(it);
-}
-
 void TransactionTracing::tryPushToTracingThread() {
     // Try to acquire the lock from main thread.
     if (mMainThreadLock.try_lock()) {
         // We got the lock! Collect any pending transactions and continue.
-        mCommittedTransactions.insert(mCommittedTransactions.end(),
-                                      std::make_move_iterator(mPendingTransactions.begin()),
-                                      std::make_move_iterator(mPendingTransactions.end()));
-        mPendingTransactions.clear();
-        mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
-                              mPendingRemovedLayers.end());
-        mPendingRemovedLayers.clear();
+        mUpdates.insert(mUpdates.end(), std::make_move_iterator(mPendingUpdates.begin()),
+                        std::make_move_iterator(mPendingUpdates.end()));
+        mPendingUpdates.clear();
+        mDestroyedLayers.insert(mDestroyedLayers.end(), mPendingDestroyedLayers.begin(),
+                                mPendingDestroyedLayers.end());
+        mPendingDestroyedLayers.clear();
         mTransactionsAvailableCv.notify_one();
         mMainThreadLock.unlock();
     } else {
@@ -367,19 +269,29 @@
     // Merge layer states to starting transaction state.
     for (const proto::TransactionState& transaction : removedEntry.transactions()) {
         for (const proto::LayerState& layerState : transaction.layer_changes()) {
-            auto it = mStartingStates.find((int32_t)layerState.layer_id());
+            auto it = mStartingStates.find(layerState.layer_id());
             if (it == mStartingStates.end()) {
-                ALOGW("Could not find layer id %d", (int32_t)layerState.layer_id());
+                // TODO(b/238781169) make this log fatal when we switch over to using new fe
+                ALOGW("Could not find layer id %d", layerState.layer_id());
                 continue;
             }
             mProtoParser.mergeFromProto(layerState, it->second);
         }
     }
 
+    for (const uint32_t destroyedLayerHandleId : removedEntry.destroyed_layer_handles()) {
+        mRemovedLayerHandlesAtStart.insert(destroyedLayerHandleId);
+    }
+
     // Clean up stale starting states since the layer has been removed and the buffer does not
     // contain any references to the layer.
-    for (const int32_t removedLayerId : removedEntry.removed_layers()) {
-        mStartingStates.erase(removedLayerId);
+    for (const uint32_t destroyedLayerId : removedEntry.destroyed_layers()) {
+        mStartingStates.erase(destroyedLayerId);
+        mRemovedLayerHandlesAtStart.erase(destroyedLayerId);
+    }
+
+    if (removedEntry.displays_changed()) {
+        mProtoParser.fromProto(removedEntry.displays(), mStartingDisplayInfos);
     }
 }
 
@@ -401,6 +313,17 @@
     transactionProto.set_vsync_id(0);
     transactionProto.set_post_time(mStartingTimestamp);
     entryProto->mutable_transactions()->Add(std::move(transactionProto));
+
+    entryProto->mutable_destroyed_layer_handles()->Reserve(
+            static_cast<int32_t>(mRemovedLayerHandlesAtStart.size()));
+    for (const uint32_t destroyedLayerHandleId : mRemovedLayerHandlesAtStart) {
+        entryProto->mutable_destroyed_layer_handles()->Add(destroyedLayerHandleId);
+    }
+
+    entryProto->mutable_displays()->Reserve(static_cast<int32_t>(mStartingDisplayInfos.size()));
+    for (auto& [layerStack, displayInfo] : mStartingDisplayInfos) {
+        entryProto->mutable_displays()->Add(mProtoParser.toProto(displayInfo, layerStack.id));
+    }
 }
 
 proto::TransactionTraceFile TransactionTracing::writeToProto() {
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index 4c291f9..f27e7a9 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -25,8 +25,12 @@
 #include <mutex>
 #include <thread>
 
-#include "RingBuffer.h"
+#include "Display/DisplayMap.h"
+#include "FrontEnd/DisplayInfo.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
 #include "LocklessStack.h"
+#include "RingBuffer.h"
 #include "TransactionProtoParser.h"
 
 using namespace android::surfaceflinger;
@@ -55,21 +59,22 @@
     ~TransactionTracing();
 
     void addQueuedTransaction(const TransactionState&);
-    void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+    void addCommittedTransactions(
+            int64_t vsyncId, nsecs_t commitTime, frontend::Update& update,
+            const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displayInfos,
+            bool displayInfoChanged);
     status_t writeToFile(std::string filename = FILE_NAME);
     void setBufferSize(size_t bufferSizeInBytes);
-    void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
-                      int parentId);
-    void onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
-                            int mirrorFromId);
     void onLayerRemoved(int layerId);
-    void onHandleRemoved(BBinder* layerHandle);
     void dump(std::string&) const;
     static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
     static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+    // version 1 - switching to support new frontend
+    static constexpr auto TRACING_VERSION = 1;
 
 private:
     friend class TransactionTracingTest;
+    friend class SurfaceFlinger;
 
     static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
 
@@ -81,15 +86,13 @@
             GUARDED_BY(mTraceLock);
     LocklessStack<proto::TransactionState> mTransactionQueue;
     nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
-    std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
-    std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+    std::unordered_map<int, proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+    std::map<uint32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mStartingDisplayInfos
             GUARDED_BY(mTraceLock);
-    std::vector<int32_t /* layerId */> mRemovedLayerHandles GUARDED_BY(mTraceLock);
-    std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
-    TransactionProtoParser mProtoParser GUARDED_BY(mTraceLock);
-    // Parses the transaction to proto without holding any tracing locks so we can generate proto
-    // in the binder thread without any contention.
-    TransactionProtoParser mLockfreeProtoParser;
+
+    std::set<uint32_t /* layerId */> mRemovedLayerHandlesAtStart GUARDED_BY(mTraceLock);
+    TransactionProtoParser mProtoParser;
 
     // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
     // otherwise will push data to temporary container.
@@ -98,26 +101,29 @@
     bool mDone GUARDED_BY(mMainThreadLock) = false;
     std::condition_variable mTransactionsAvailableCv;
     std::condition_variable mTransactionsAddedToBufferCv;
-    struct CommittedTransactions {
+    struct CommittedUpdates {
         std::vector<uint64_t> transactionIds;
+        std::vector<LayerCreationArgs> createdLayers;
+        std::vector<uint32_t> destroyedLayerHandles;
+        bool displayInfoChanged;
+        display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
         int64_t vsyncId;
         int64_t timestamp;
     };
-    std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
-    std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+    std::vector<CommittedUpdates> mUpdates GUARDED_BY(mMainThreadLock);
+    std::vector<CommittedUpdates> mPendingUpdates; // only accessed by main thread
 
-    std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
-    std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+    std::vector<uint32_t /* layerId */> mDestroyedLayers GUARDED_BY(mMainThreadLock);
+    std::vector<uint32_t /* layerId */> mPendingDestroyedLayers; // only accessed by main thread
 
     proto::TransactionTraceFile createTraceFileProto() const;
     void loop();
-    void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
-                  const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+    void addEntry(const std::vector<CommittedUpdates>& committedTransactions,
+                  const std::vector<uint32_t>& removedLayers) EXCLUDES(mTraceLock);
     int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
     void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
     void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
     void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
-
     // TEST
     // Wait until all the committed transactions for the specified vsync id are added to the buffer.
     void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index e8fe734..b6435a8 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -25,8 +25,8 @@
     name: "layertracegenerator",
     defaults: [
         "libsurfaceflinger_mocks_defaults",
+        "librenderengine_deps",
         "surfaceflinger_defaults",
-        "skia_renderengine_deps",
     ],
     srcs: [
         ":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index cf44eff..55004c5 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -16,268 +16,156 @@
 
 #undef LOG_TAG
 #define LOG_TAG "LayerTraceGenerator"
+//#define LOG_NDEBUG 0
 
-#include <TestableSurfaceFlinger.h>
 #include <Tracing/TransactionProtoParser.h>
-#include <binder/IPCThreadState.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
 #include <gui/LayerState.h>
 #include <log/log.h>
-#include <mock/MockEventThread.h>
 #include <renderengine/ExternalTexture.h>
-#include <renderengine/mock/FakeExternalTexture.h>
-#include <renderengine/mock/RenderEngine.h>
 #include <utils/String16.h>
+#include <filesystem>
+#include <fstream>
+#include <ios>
 #include <string>
+#include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/RequestedLayerState.h"
+#include "LayerProtoHelper.h"
+#include "Tracing/LayerTracing.h"
+#include "TransactionState.h"
+#include "cutils/properties.h"
 
 #include "LayerTraceGenerator.h"
 
 namespace android {
-
-class Factory final : public surfaceflinger::Factory {
-public:
-    ~Factory() = default;
-
-    std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
-
-    std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
-            Fps /*currentRefreshRate*/) override {
-        return std::make_unique<scheduler::FakePhaseOffsets>();
-    }
-
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return new android::impl::SurfaceInterceptor();
-    }
-
-    sp<StartPropertySetThread> createStartPropertySetThread(
-            bool /* timestampPropertyValue */) override {
-        return nullptr;
-    }
-
-    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& /* creationArgs */) override {
-        return nullptr;
-    }
-
-    sp<GraphicBuffer> createGraphicBuffer(uint32_t /* width */, uint32_t /* height */,
-                                          PixelFormat /* format */, uint32_t /* layerCount */,
-                                          uint64_t /* usage */,
-                                          std::string /* requestorName */) override {
-        return nullptr;
-    }
-
-    void createBufferQueue(sp<IGraphicBufferProducer>* /* outProducer */,
-                           sp<IGraphicBufferConsumer>* /* outConsumer */,
-                           bool /* consumerIsSurfaceFlinger */) override {}
-
-    sp<IGraphicBufferProducer> createMonitoredProducer(
-            const sp<IGraphicBufferProducer>& /* producer */,
-            const sp<SurfaceFlinger>& /* flinger */, const wp<Layer>& /* layer */) override {
-        return nullptr;
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(
-            const sp<IGraphicBufferConsumer>& /* consumer */,
-            renderengine::RenderEngine& /* renderEngine */, uint32_t /* textureName */,
-            Layer* /* layer */) override {
-        return nullptr;
-    }
-
-    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
-            const sp<IGraphicBufferProducer>& /* producer */) override {
-        return nullptr;
-    }
-
-    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
-        return compositionengine::impl::createCompositionEngine();
-    }
-
-    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) {
-        return sp<ContainerLayer>::make(args);
-    }
-
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) {
-        return new BufferStateLayer(args);
-    }
-
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) {
-        return new EffectLayer(args);
-    }
-
-    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
-        return nullptr;
-    }
-
-    std::unique_ptr<FrameTracer> createFrameTracer() override {
-        return std::make_unique<testing::NiceMock<mock::FrameTracer>>();
-    }
-
-    std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
-            std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
-        return std::make_unique<testing::NiceMock<mock::FrameTimeline>>(timeStats,
-                                                                        surfaceFlingerPid);
-    }
-};
-
-class MockSurfaceFlinger : public SurfaceFlinger {
-public:
-    MockSurfaceFlinger(Factory& factory)
-          : SurfaceFlinger(factory, SurfaceFlinger::SkipInitialization) {}
-    std::shared_ptr<renderengine::ExternalTexture> getExternalTextureFromBufferData(
-            const BufferData& bufferData, const char* /* layerName */) const override {
-        return std::make_shared<renderengine::mock::FakeExternalTexture>(bufferData.getWidth(),
-                                                                         bufferData.getHeight(),
-                                                                         bufferData.getId(),
-                                                                         bufferData
-                                                                                 .getPixelFormat(),
-                                                                         bufferData.getUsage());
-    };
-
-    // b/220017192 migrate from transact codes to ISurfaceComposer apis
-    void setLayerTracingFlags(int32_t flags) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(flags);
-        transact(1033, data, &reply, 0 /* flags */);
-    }
-
-    void startLayerTracing(int64_t traceStartTime) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(1);
-        data.writeInt64(traceStartTime);
-        transact(1025, data, &reply, 0 /* flags */);
-    }
-
-    void stopLayerTracing(const char* tracePath) {
-        Parcel data;
-        Parcel reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        data.writeInt32(2);
-        data.writeCString(tracePath);
-        transact(1025, data, &reply, 0 /* flags */);
-    }
-};
-
-class TraceGenFlingerDataMapper : public TransactionProtoParser::FlingerDataMapper {
-public:
-    std::unordered_map<int32_t /*layerId*/, sp<IBinder> /* handle */> mLayerHandles;
-    sp<IBinder> getLayerHandle(int32_t layerId) const override {
-        if (layerId == -1) {
-            ALOGE("Error: Called with layer=%d", layerId);
-            return nullptr;
-        }
-        auto it = mLayerHandles.find(layerId);
-        if (it == mLayerHandles.end()) {
-            ALOGE("Error: Could not find handle for layer=%d", layerId);
-            return nullptr;
-        }
-        return it->second;
-    }
-};
+using namespace ftl::flag_operators;
 
 bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile,
                                    const char* outputLayersTracePath) {
     if (traceFile.entry_size() == 0) {
+        ALOGD("Trace file is empty");
         return false;
     }
 
-    Factory mFactory;
-    sp<MockSurfaceFlinger> flinger = new MockSurfaceFlinger(mFactory);
-    TestableSurfaceFlinger mFlinger(flinger);
-    mFlinger.setupRenderEngine(
-            std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>());
-    mock::VsyncController* mVsyncController = new testing::NiceMock<mock::VsyncController>();
-    mock::VSyncTracker* mVSyncTracker = new testing::NiceMock<mock::VSyncTracker>();
-    mock::EventThread* mEventThread = new testing::NiceMock<mock::EventThread>();
-    mock::EventThread* mSFEventThread = new testing::NiceMock<mock::EventThread>();
-    mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
-                            std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
-                            std::unique_ptr<EventThread>(mEventThread),
-                            std::unique_ptr<EventThread>(mSFEventThread),
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            TestableSurfaceFlinger::kOneDisplayMode, true /* useNiceMock */);
+    TransactionProtoParser parser(std::make_unique<TransactionProtoParser::FlingerDataMapper>());
 
-    Hwc2::mock::Composer* mComposer = new testing::NiceMock<Hwc2::mock::Composer>();
-    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-    mFlinger.mutableMaxRenderTargetSize() = 16384;
+    // frontend
+    frontend::LayerLifecycleManager lifecycleManager;
+    frontend::LayerHierarchyBuilder hierarchyBuilder{{}};
+    frontend::LayerSnapshotBuilder snapshotBuilder;
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
 
-    flinger->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
-    flinger->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos());
-    std::unique_ptr<TraceGenFlingerDataMapper> mapper =
-            std::make_unique<TraceGenFlingerDataMapper>();
-    TraceGenFlingerDataMapper* dataMapper = mapper.get();
-    TransactionProtoParser parser(std::move(mapper));
+    renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    bool supportsBlur = atoi(value);
 
-    nsecs_t frameTime;
-    int64_t vsyncId;
+    LayerTracing layerTracing;
+    layerTracing.setTraceFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS);
+    // 10MB buffer size (large enough to hold a single entry)
+    layerTracing.setBufferSize(10 * 1024 * 1024);
+    layerTracing.enable();
+    layerTracing.writeToFile(outputLayersTracePath);
+    std::ofstream out(outputLayersTracePath, std::ios::binary | std::ios::app);
+
     ALOGD("Generating %d transactions...", traceFile.entry_size());
     for (int i = 0; i < traceFile.entry_size(); i++) {
+        // parse proto
         proto::TransactionTraceEntry entry = traceFile.entry(i);
         ALOGV("    Entry %04d/%04d for time=%" PRId64 " vsyncid=%" PRId64
-              " layers +%d -%d transactions=%d",
+              " layers +%d -%d handles -%d transactions=%d",
               i, traceFile.entry_size(), entry.elapsed_realtime_nanos(), entry.vsync_id(),
-              entry.added_layers_size(), entry.removed_layers_size(), entry.transactions_size());
+              entry.added_layers_size(), entry.destroyed_layers_size(),
+              entry.destroyed_layer_handles_size(), entry.transactions_size());
 
+        std::vector<std::unique_ptr<frontend::RequestedLayerState>> addedLayers;
+        addedLayers.reserve((size_t)entry.added_layers_size());
         for (int j = 0; j < entry.added_layers_size(); j++) {
-            // create layers
-            TracingLayerCreationArgs tracingArgs;
-            parser.fromProto(entry.added_layers(j), tracingArgs);
-
-            sp<IBinder> outHandle;
-            int32_t outLayerId;
-            LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name,
-                                   tracingArgs.flags, LayerMetadata());
-            args.sequence = std::make_optional<int32_t>(tracingArgs.layerId);
-
-            if (tracingArgs.mirrorFromId == -1) {
-                sp<IBinder> parentHandle = nullptr;
-                if ((tracingArgs.parentId != -1) &&
-                    (dataMapper->mLayerHandles.find(tracingArgs.parentId) ==
-                     dataMapper->mLayerHandles.end())) {
-                    args.addToRoot = false;
-                } else {
-                    parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId);
-                }
-                mFlinger.createLayer(args, &outHandle, parentHandle, &outLayerId,
-                                     nullptr /* parentLayer */, nullptr /* outTransformHint */);
-            } else {
-                sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId);
-                mFlinger.mirrorLayer(args, mirrorFromHandle, &outHandle, &outLayerId);
-            }
-            LOG_ALWAYS_FATAL_IF(outLayerId != tracingArgs.layerId,
-                                "Could not create layer expected:%d actual:%d", tracingArgs.layerId,
-                                outLayerId);
-            dataMapper->mLayerHandles[tracingArgs.layerId] = outHandle;
+            LayerCreationArgs args;
+            parser.fromProto(entry.added_layers(j), args);
+            ALOGV("       %s", args.getDebugString().c_str());
+            addedLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args));
         }
 
+        std::vector<TransactionState> transactions;
+        transactions.reserve((size_t)entry.transactions_size());
         for (int j = 0; j < entry.transactions_size(); j++) {
             // apply transactions
             TransactionState transaction = parser.fromProto(entry.transactions(j));
-            mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                         transaction.displays, transaction.flags,
-                                         transaction.applyToken, transaction.inputWindowCommands,
-                                         transaction.desiredPresentTime,
-                                         transaction.isAutoTimestamp, {},
-                                         transaction.hasListenerCallbacks,
-                                         transaction.listenerCallbacks, transaction.id);
+            for (auto& resolvedComposerState : transaction.states) {
+                if (resolvedComposerState.state.what & layer_state_t::eInputInfoChanged) {
+                    if (!resolvedComposerState.state.windowInfoHandle->getInfo()->inputConfig.test(
+                                gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
+                        // create a fake token since the FE expects a valid token
+                        resolvedComposerState.state.windowInfoHandle->editInfo()->token =
+                                sp<BBinder>::make();
+                    }
+                }
+            }
+            transactions.emplace_back(std::move(transaction));
         }
 
-        for (int j = 0; j < entry.removed_layer_handles_size(); j++) {
-            dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j));
+        for (int j = 0; j < entry.destroyed_layers_size(); j++) {
+            ALOGV("       destroyedHandles=%d", entry.destroyed_layers(j));
         }
 
-        frameTime = entry.elapsed_realtime_nanos();
-        vsyncId = entry.vsync_id();
-        mFlinger.commit(frameTime, vsyncId);
+        std::vector<uint32_t> destroyedHandles;
+        destroyedHandles.reserve((size_t)entry.destroyed_layer_handles_size());
+        for (int j = 0; j < entry.destroyed_layer_handles_size(); j++) {
+            ALOGV("       destroyedHandles=%d", entry.destroyed_layer_handles(j));
+            destroyedHandles.push_back(entry.destroyed_layer_handles(j));
+        }
+
+        bool displayChanged = entry.displays_changed();
+        if (displayChanged) {
+            parser.fromProto(entry.displays(), displayInfos);
+        }
+
+        // apply updates
+        lifecycleManager.addLayers(std::move(addedLayers));
+        lifecycleManager.applyTransactions(transactions, /*ignoreUnknownHandles=*/true);
+        lifecycleManager.onHandlesDestroyed(destroyedHandles, /*ignoreUnknownHandles=*/true);
+
+        if (lifecycleManager.getGlobalChanges().test(
+                    frontend::RequestedLayerState::Changes::Hierarchy)) {
+            hierarchyBuilder.update(lifecycleManager.getLayers(),
+                                    lifecycleManager.getDestroyedLayers());
+        }
+
+        frontend::LayerSnapshotBuilder::Args args{.root = hierarchyBuilder.getHierarchy(),
+                                                  .layerLifecycleManager = lifecycleManager,
+                                                  .displays = displayInfos,
+                                                  .displayChanges = displayChanged,
+                                                  .globalShadowSettings = globalShadowSettings,
+                                                  .supportsBlur = supportsBlur,
+                                                  .forceFullDamage = false,
+                                                  .supportedLayerGenericMetadata = {},
+                                                  .genericLayerMetadataKeyMap = {}};
+        snapshotBuilder.update(args);
+
+        bool visibleRegionsDirty = lifecycleManager.getGlobalChanges().any(
+                frontend::RequestedLayerState::Changes::VisibleRegion |
+                frontend::RequestedLayerState::Changes::Hierarchy |
+                frontend::RequestedLayerState::Changes::Visibility);
+
+        ALOGV("    layers:%04zu snapshots:%04zu changes:%s", lifecycleManager.getLayers().size(),
+              snapshotBuilder.getSnapshots().size(),
+              lifecycleManager.getGlobalChanges().string().c_str());
+
+        lifecycleManager.commitChanges();
+
+        LayersProto layersProto = LayerProtoFromSnapshotGenerator(snapshotBuilder, displayInfos, {},
+                                                                  layerTracing.getFlags())
+                                          .generate(hierarchyBuilder.getHierarchy());
+        auto displayProtos = LayerProtoHelper::writeDisplayInfoToProto(displayInfos);
+        layerTracing.notify(visibleRegionsDirty, entry.elapsed_realtime_nanos(), entry.vsync_id(),
+                            &layersProto, {}, &displayProtos);
+        layerTracing.appendToStream(out);
     }
-
-    flinger->stopLayerTracing(outputLayersTracePath);
+    layerTracing.disable("", /*writeToFile=*/false);
+    out.close();
     ALOGD("End of generating trace file. File written to %s", outputLayersTracePath);
-    dataMapper->mLayerHandles.clear();
     return true;
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/main.cpp b/services/surfaceflinger/Tracing/tools/main.cpp
index f3cf42d..c440c19 100644
--- a/services/surfaceflinger/Tracing/tools/main.cpp
+++ b/services/surfaceflinger/Tracing/tools/main.cpp
@@ -52,6 +52,7 @@
     ;
     ALOGD("Generating %s...", outputLayersTracePath);
     std::cout << "Generating " << outputLayersTracePath << "\n";
+
     if (!LayerTraceGenerator().generate(transactionTraceFile, outputLayersTracePath)) {
         std::cout << "Error: Failed to generate layers trace " << outputLayersTracePath;
         return -1;
diff --git a/services/surfaceflinger/Tracing/tools/run.sh b/services/surfaceflinger/Tracing/tools/run.sh
index baa93f1..307a4d8 100644
--- a/services/surfaceflinger/Tracing/tools/run.sh
+++ b/services/surfaceflinger/Tracing/tools/run.sh
@@ -5,7 +5,15 @@
 # Build, push and run layertracegenerator
 $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode layertracegenerator
 adb wait-for-device && adb push $OUT/system/bin/layertracegenerator /data/layertracegenerator
-echo "Writing transaction trace to file"
-adb shell service call SurfaceFlinger 1041 i32 0
-adb shell /data/layertracegenerator
+
+if [ -z "$1" ]
+  then
+    echo "Writing transaction trace to file"
+    adb shell service call SurfaceFlinger 1041 i32 0
+    adb shell /data/layertracegenerator
+  else
+    echo "Pushing transaction trace to device"
+    adb push $1 /data/transaction_trace.winscope
+    adb shell /data/layertracegenerator /data/transaction_trace.winscope
+fi
 adb pull /data/misc/wmtrace/layers_trace.winscope
\ No newline at end of file
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index d2c2e29..3587a72 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -92,11 +92,6 @@
     return NO_ERROR;
 }
 
-status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
-        const sp<CallbackHandle>& handle) {
-    return addCallbackHandle(handle, std::vector<JankData>());
-}
-
 status_t TransactionCallbackInvoker::findOrCreateTransactionStats(
         const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
         TransactionStats** outTransactionStats) {
@@ -137,7 +132,6 @@
             sp<Fence> currentFence = future.get().value_or(Fence::NO_FENCE);
             if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
                 prevFence = std::move(currentFence);
-                handle->previousReleaseFence = prevFence;
             } else if (prevFence != nullptr) {
                 // If both fences are signaled or both are unsignaled, we need to merge
                 // them to get an accurate timestamp.
@@ -147,8 +141,7 @@
                     snprintf(fenceName, 32, "%.28s", handle->name.c_str());
                     sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
                     if (mergedFence->isValid()) {
-                        handle->previousReleaseFence = std::move(mergedFence);
-                        prevFence = handle->previousReleaseFence;
+                        prevFence = std::move(mergedFence);
                     }
                 } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
                     // If one fence has signaled and the other hasn't, the unsignaled
@@ -158,10 +151,11 @@
                     // by this point, they will have both signaled and only the timestamp
                     // will be slightly off; any dependencies after this point will
                     // already have been met.
-                    handle->previousReleaseFence = std::move(currentFence);
+                    prevFence = std::move(currentFence);
                 }
             }
         }
+        handle->previousReleaseFence = prevFence;
         handle->previousReleaseFences.clear();
 
         FrameEventHistoryStats eventStats(handle->frameNumber,
@@ -178,8 +172,8 @@
     return NO_ERROR;
 }
 
-void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) {
-    mPresentFence = presentFence;
+void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) {
+    mPresentFence = std::move(presentFence);
 }
 
 void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 81d79f0..3074795 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -19,6 +19,7 @@
 #include <condition_variable>
 #include <deque>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <thread>
 #include <unordered_map>
@@ -26,10 +27,10 @@
 
 #include <android-base/thread_annotations.h>
 #include <binder/IBinder.h>
-#include <compositionengine/FenceResult.h>
 #include <ftl/future.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <ui/Fence.h>
+#include <ui/FenceResult.h>
 
 namespace android {
 
@@ -48,7 +49,7 @@
     std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences;
     std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
     nsecs_t latchTime = -1;
-    uint32_t transformHint = 0;
+    std::optional<uint32_t> transformHint = std::nullopt;
     uint32_t currentMaxAcquiredBufferCount = 0;
     std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
     CompositorTiming compositorTiming;
@@ -65,12 +66,9 @@
     status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
                                              std::deque<sp<CallbackHandle>>& outRemainingHandles);
 
-    // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
-    // presented this frame.
-    status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
     void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
 
-    void addPresentFence(const sp<Fence>& presentFence);
+    void addPresentFence(sp<Fence>);
 
     void sendCallbacks(bool onCommitOnly);
     void clearCompletedTransactions() {
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 900d566..7132a59 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -20,60 +20,96 @@
 #include <memory>
 #include <mutex>
 #include <vector>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "renderengine/ExternalTexture.h"
 
 #include <gui/LayerState.h>
 #include <system/window.h>
 
 namespace android {
 
-class CountDownLatch;
+enum TraverseBuffersReturnValues {
+    CONTINUE_TRAVERSAL,
+    STOP_TRAVERSAL,
+    DELETE_AND_CONTINUE_TRAVERSAL,
+};
+
+// Extends the client side composer state by resolving buffer.
+class ResolvedComposerState : public ComposerState {
+public:
+    ResolvedComposerState() = default;
+    ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
+    std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+    uint32_t layerId = UNASSIGNED_LAYER_ID;
+    uint32_t parentId = UNASSIGNED_LAYER_ID;
+    uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
+    uint32_t touchCropId = UNASSIGNED_LAYER_ID;
+};
 
 struct TransactionState {
     TransactionState() = default;
 
     TransactionState(const FrameTimelineInfo& frameTimelineInfo,
-                     const Vector<ComposerState>& composerStates,
+                     std::vector<ResolvedComposerState>& composerStates,
                      const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                      const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
                      int64_t desiredPresentTime, bool isAutoTimestamp,
-                     const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+                     std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
                      bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
-                     int originPid, int originUid, uint64_t transactionId)
+                     int originPid, int originUid, uint64_t transactionId,
+                     std::vector<uint64_t> mergedTransactionIds)
           : frameTimelineInfo(frameTimelineInfo),
-            states(composerStates),
+            states(std::move(composerStates)),
             displays(displayStates),
             flags(transactionFlags),
             applyToken(applyToken),
             inputWindowCommands(inputWindowCommands),
             desiredPresentTime(desiredPresentTime),
             isAutoTimestamp(isAutoTimestamp),
-            buffer(uncacheBuffer),
+            uncacheBufferIds(std::move(uncacheBufferIds)),
             postTime(postTime),
-            permissions(permissions),
             hasListenerCallbacks(hasListenerCallbacks),
             listenerCallbacks(listenerCallbacks),
             originPid(originPid),
             originUid(originUid),
-            id(transactionId) {}
+            id(transactionId),
+            mergedTransactionIds(std::move(mergedTransactionIds)) {}
 
     // Invokes `void(const layer_state_t&)` visitor for matching layers.
     template <typename Visitor>
     void traverseStatesWithBuffers(Visitor&& visitor) const {
-        for (const auto& [state] : states) {
-            if (state.hasBufferChanges() && state.hasValidBuffer() && state.surface) {
-                visitor(state);
+        for (const auto& state : states) {
+            if (state.state.hasBufferChanges() && state.externalTexture && state.state.surface) {
+                visitor(state.state);
             }
         }
     }
 
+    template <typename Visitor>
+    void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
+        for (auto state = states.begin(); state != states.end();) {
+            if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
+                int result = visitor(state->state, state->externalTexture);
+                if (result == STOP_TRAVERSAL) return;
+                if (result == DELETE_AND_CONTINUE_TRAVERSAL) {
+                    state = states.erase(state);
+                    continue;
+                }
+            }
+            state++;
+        }
+    }
+
     // TODO(b/185535769): Remove FrameHint. Instead, reset the idle timer (of the relevant physical
     // display) on the main thread if commit leads to composite. Then, RefreshRateOverlay should be
     // able to setFrameRate once, rather than for each transaction.
     bool isFrameActive() const {
         if (!displays.empty()) return true;
 
-        for (const auto& [state] : states) {
-            if (state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
+        for (const auto& state : states) {
+            const bool frameRateChanged = state.state.what & layer_state_t::eFrameRateChanged;
+            if (!frameRateChanged ||
+                state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
                 return true;
             }
         }
@@ -82,66 +118,22 @@
     }
 
     FrameTimelineInfo frameTimelineInfo;
-    Vector<ComposerState> states;
+    std::vector<ResolvedComposerState> states;
     Vector<DisplayState> displays;
     uint32_t flags;
     sp<IBinder> applyToken;
     InputWindowCommands inputWindowCommands;
     int64_t desiredPresentTime;
     bool isAutoTimestamp;
-    client_cache_t buffer;
+    std::vector<uint64_t> uncacheBufferIds;
     int64_t postTime;
-    uint32_t permissions;
     bool hasListenerCallbacks;
     std::vector<ListenerCallbacks> listenerCallbacks;
     int originPid;
     int originUid;
     uint64_t id;
-    std::shared_ptr<CountDownLatch> transactionCommittedSignal;
-    int64_t queueTime = 0;
     bool sentFenceTimeoutWarning = false;
-};
-
-class CountDownLatch {
-public:
-    enum {
-        eSyncTransaction = 1 << 0,
-        eSyncInputWindows = 1 << 1,
-    };
-    explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
-
-    // True if there is no waiting condition after count down.
-    bool countDown(uint32_t flag) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        if (mFlags == 0) {
-            return true;
-        }
-        mFlags &= ~flag;
-        if (mFlags == 0) {
-            mCountDownComplete.notify_all();
-            return true;
-        }
-        return false;
-    }
-
-    // Return true if triggered.
-    bool wait_until(const std::chrono::nanoseconds& timeout) const {
-        std::unique_lock<std::mutex> lock(mMutex);
-        const auto untilTime = std::chrono::system_clock::now() + timeout;
-        while (mFlags != 0) {
-            // Conditional variables can be woken up sporadically, so we check count
-            // to verify the wakeup was triggered by |countDown|.
-            if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-private:
-    uint32_t mFlags;
-    mutable std::condition_variable mCountDownComplete;
-    mutable std::mutex mMutex;
+    std::vector<uint64_t> mergedTransactionIds;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
index 4497caf..bc9b870 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.cpp
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
@@ -59,7 +59,7 @@
 
 void TunnelModeEnabledReporter::addListener(const sp<gui::ITunnelModeEnabledListener>& listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
-    asBinder->linkToDeath(this);
+    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
     bool tunnelModeEnabled = false;
     {
         std::scoped_lock lock(mMutex);
diff --git a/services/surfaceflinger/Utils/Dumper.h b/services/surfaceflinger/Utils/Dumper.h
new file mode 100644
index 0000000..ee94217
--- /dev/null
+++ b/services/surfaceflinger/Utils/Dumper.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <string>
+#include <string_view>
+
+#include <ftl/optional.h>
+
+namespace android::utils {
+
+// Dumps variables by appending their name and value to the output string. A variable is formatted
+// as "name=value". If the name or value is empty, the format is "value" or "name=", respectively.
+// A value of user-defined type T is stringified via `std::string to_string(const T&)`, which must
+// be defined in the same namespace as T per the rules of ADL (argument-dependent lookup).
+//
+// TODO(b/249828573): Consolidate with <compositionengine/impl/DumpHelpers.h>
+class Dumper {
+public:
+    explicit Dumper(std::string& out) : mOut(out) {}
+
+    void eol() { mOut += '\n'; }
+
+    void dump(std::string_view name, std::string_view value = {}) {
+        using namespace std::string_view_literals;
+
+        for (int i = mIndent; i-- > 0;) mOut += "    "sv;
+        mOut += name;
+        if (!name.empty()) mOut += '=';
+        mOut += value;
+        eol();
+    }
+
+    void dump(std::string_view name, const std::string& value) {
+        dump(name, static_cast<const std::string_view&>(value));
+    }
+
+    void dump(std::string_view name, bool value) {
+        using namespace std::string_view_literals;
+        dump(name, value ? "true"sv : "false"sv);
+    }
+
+    template <typename T>
+    void dump(std::string_view name, const std::optional<T>& opt) {
+        if (opt) {
+            dump(name, *opt);
+        } else {
+            using namespace std::string_view_literals;
+            dump(name, "nullopt"sv);
+        }
+    }
+
+    template <typename T>
+    void dump(std::string_view name, const ftl::Optional<T>& opt) {
+        dump(name, static_cast<const std::optional<T>&>(opt));
+    }
+
+    template <typename T, typename... Ts>
+    void dump(std::string_view name, const T& value, const Ts&... rest) {
+        std::string string;
+
+        constexpr bool kIsTuple = sizeof...(Ts) > 0;
+        if constexpr (kIsTuple) {
+            string += '{';
+        }
+
+        using std::to_string;
+        string += to_string(value);
+
+        if constexpr (kIsTuple) {
+            string += ((", " + to_string(rest)) + ...);
+            string += '}';
+        }
+
+        dump(name, string);
+    }
+
+    struct Indent {
+        explicit Indent(Dumper& dumper) : dumper(dumper) { dumper.mIndent++; }
+        ~Indent() { dumper.mIndent--; }
+
+        Dumper& dumper;
+    };
+
+    struct Section {
+        Section(Dumper& dumper, std::string_view heading) : dumper(dumper) {
+            dumper.dump({}, heading);
+            indent.emplace(dumper);
+        }
+
+        ~Section() {
+            indent.reset();
+            dumper.eol();
+        }
+
+        Dumper& dumper;
+        std::optional<Indent> indent;
+    };
+
+private:
+    std::string& mOut;
+    int mIndent = 0;
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index 023402f..20699ef 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -16,8 +16,11 @@
 
 #include <ftl/small_vector.h>
 #include <gui/ISurfaceComposer.h>
+#include <gui/TraceUtils.h>
+#include <gui/WindowInfosUpdate.h>
+#include <scheduler/Time.h>
 
-#include "SurfaceFlinger.h"
+#include "BackgroundExecutor.h"
 #include "WindowInfosListenerInvoker.h"
 
 namespace android {
@@ -26,32 +29,46 @@
 using gui::IWindowInfosListener;
 using gui::WindowInfo;
 
-struct WindowInfosListenerInvoker::WindowInfosReportedListener
-      : gui::BnWindowInfosReportedListener {
-    explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker, size_t callbackCount,
-                                         bool shouldSync)
-          : mInvoker(invoker), mCallbacksPending(callbackCount), mShouldSync(shouldSync) {}
+using WindowInfosListenerVector = ftl::SmallVector<const sp<gui::IWindowInfosListener>, 3>;
+
+struct WindowInfosReportedListenerInvoker : gui::BnWindowInfosReportedListener,
+                                            IBinder::DeathRecipient {
+    WindowInfosReportedListenerInvoker(WindowInfosListenerVector windowInfosListeners,
+                                       WindowInfosReportedListenerSet windowInfosReportedListeners)
+          : mCallbacksPending(windowInfosListeners.size()),
+            mWindowInfosListeners(std::move(windowInfosListeners)),
+            mWindowInfosReportedListeners(std::move(windowInfosReportedListeners)) {}
 
     binder::Status onWindowInfosReported() override {
-        mCallbacksPending--;
-        if (mCallbacksPending == 0) {
-            mInvoker.windowInfosReported(mShouldSync);
+        if (--mCallbacksPending == 0) {
+            for (const auto& listener : mWindowInfosReportedListeners) {
+                sp<IBinder> asBinder = IInterface::asBinder(listener);
+                if (asBinder->isBinderAlive()) {
+                    listener->onWindowInfosReported();
+                }
+            }
+
+            auto wpThis = wp<WindowInfosReportedListenerInvoker>::fromExisting(this);
+            for (const auto& listener : mWindowInfosListeners) {
+                sp<IBinder> binder = IInterface::asBinder(listener);
+                binder->unlinkToDeath(wpThis);
+            }
         }
         return binder::Status::ok();
     }
 
-private:
-    WindowInfosListenerInvoker& mInvoker;
-    std::atomic<size_t> mCallbacksPending;
-    bool mShouldSync;
-};
+    void binderDied(const wp<IBinder>&) { onWindowInfosReported(); }
 
-WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger)
-      : mFlinger(flinger) {}
+private:
+    std::atomic<size_t> mCallbacksPending;
+    static constexpr size_t kStaticCapacity = 3;
+    const WindowInfosListenerVector mWindowInfosListeners;
+    WindowInfosReportedListenerSet mWindowInfosReportedListeners;
+};
 
 void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
     sp<IBinder> asBinder = IInterface::asBinder(listener);
-    asBinder->linkToDeath(this);
+    asBinder->linkToDeath(sp<DeathRecipient>::fromExisting(this));
 
     std::scoped_lock lock(mListenersMutex);
     mWindowInfosListeners.try_emplace(asBinder, std::move(listener));
@@ -62,7 +79,7 @@
     sp<IBinder> asBinder = IInterface::asBinder(listener);
 
     std::scoped_lock lock(mListenersMutex);
-    asBinder->unlinkToDeath(this);
+    asBinder->unlinkToDeath(sp<DeathRecipient>::fromExisting(this));
     mWindowInfosListeners.erase(asBinder);
 }
 
@@ -71,34 +88,20 @@
     mWindowInfosListeners.erase(who);
 }
 
-void WindowInfosListenerInvoker::windowInfosChanged(std::vector<WindowInfo> windowInfos,
-                                                    std::vector<DisplayInfo> displayInfos,
-                                                    bool shouldSync, bool forceImmediateCall) {
-    auto callListeners = [this, windowInfos = std::move(windowInfos),
-                          displayInfos = std::move(displayInfos)](bool shouldSync) mutable {
-        ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
-        {
-            std::scoped_lock lock(mListenersMutex);
-            for (const auto& [_, listener] : mWindowInfosListeners) {
-                windowInfosListeners.push_back(listener);
-            }
-        }
-
-        auto reportedListener =
-                sp<WindowInfosReportedListener>::make(*this, windowInfosListeners.size(),
-                                                      shouldSync);
-
-        for (const auto& listener : windowInfosListeners) {
-            auto status =
-                    listener->onWindowInfosChanged(windowInfos, displayInfos, reportedListener);
-            if (!status.isOk()) {
-                reportedListener->onWindowInfosReported();
-            }
-        }
-    };
-
+void WindowInfosListenerInvoker::windowInfosChanged(
+        gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
+        bool forceImmediateCall) {
+    WindowInfosListenerVector listeners;
     {
-        std::scoped_lock lock(mMessagesMutex);
+        std::scoped_lock lock{mMessagesMutex};
+
+        if (!mDelayInfo) {
+            mDelayInfo = DelayInfo{
+                    .vsyncId = update.vsyncId,
+                    .frameTime = update.timestamp,
+            };
+        }
+
         // If there are unacked messages and this isn't a forced call, then return immediately.
         // If a forced window infos change doesn't happen first, the update will be sent after
         // the WindowInfosReportedListeners are called. If a forced window infos change happens or
@@ -106,41 +109,87 @@
         // will be dropped and the listeners will only be called with the latest info. This is done
         // to reduce the amount of binder memory used.
         if (mActiveMessageCount > 0 && !forceImmediateCall) {
-            mWindowInfosChangedDelayed = std::move(callListeners);
-            mShouldSyncDelayed |= shouldSync;
+            mDelayedUpdate = std::move(update);
+            mReportedListeners.merge(reportedListeners);
             return;
         }
 
-        mWindowInfosChangedDelayed = nullptr;
-        shouldSync |= mShouldSyncDelayed;
-        mShouldSyncDelayed = false;
+        if (mDelayedUpdate) {
+            mDelayedUpdate.reset();
+        }
+
+        {
+            std::scoped_lock lock{mListenersMutex};
+            for (const auto& [_, listener] : mWindowInfosListeners) {
+                listeners.push_back(listener);
+            }
+        }
+        if (CC_UNLIKELY(listeners.empty())) {
+            mReportedListeners.merge(reportedListeners);
+            mDelayInfo.reset();
+            return;
+        }
+
+        reportedListeners.insert(sp<WindowInfosListenerInvoker>::fromExisting(this));
+        reportedListeners.merge(mReportedListeners);
+        mReportedListeners.clear();
+
         mActiveMessageCount++;
+        updateMaxSendDelay();
+        mDelayInfo.reset();
     }
-    callListeners(shouldSync);
+
+    auto reportedInvoker =
+            sp<WindowInfosReportedListenerInvoker>::make(listeners, std::move(reportedListeners));
+
+    for (const auto& listener : listeners) {
+        sp<IBinder> asBinder = IInterface::asBinder(listener);
+
+        // linkToDeath is used here to ensure that the windowInfosReportedListeners
+        // are called even if one of the windowInfosListeners dies before
+        // calling onWindowInfosReported.
+        asBinder->linkToDeath(reportedInvoker);
+
+        auto status = listener->onWindowInfosChanged(update, reportedInvoker);
+        if (!status.isOk()) {
+            reportedInvoker->onWindowInfosReported();
+        }
+    }
 }
 
-void WindowInfosListenerInvoker::windowInfosReported(bool shouldSync) {
-    if (shouldSync) {
-        mFlinger.windowInfosReported();
-    }
-
-    std::function<void(bool)> callListeners;
-    bool shouldSyncDelayed;
-    {
-        std::scoped_lock lock{mMessagesMutex};
-        mActiveMessageCount--;
-        if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) {
-            return;
+binder::Status WindowInfosListenerInvoker::onWindowInfosReported() {
+    BackgroundExecutor::getInstance().sendCallbacks({[this]() {
+        gui::WindowInfosUpdate update;
+        {
+            std::scoped_lock lock{mMessagesMutex};
+            mActiveMessageCount--;
+            if (!mDelayedUpdate || mActiveMessageCount > 0) {
+                return;
+            }
+            update = std::move(*mDelayedUpdate);
+            mDelayedUpdate.reset();
         }
+        windowInfosChanged(std::move(update), {}, false);
+    }});
+    return binder::Status::ok();
+}
 
-        mActiveMessageCount++;
-        callListeners = std::move(mWindowInfosChangedDelayed);
-        mWindowInfosChangedDelayed = nullptr;
-        shouldSyncDelayed = mShouldSyncDelayed;
-        mShouldSyncDelayed = false;
+WindowInfosListenerInvoker::DebugInfo WindowInfosListenerInvoker::getDebugInfo() {
+    std::scoped_lock lock{mMessagesMutex};
+    updateMaxSendDelay();
+    mDebugInfo.pendingMessageCount = mActiveMessageCount;
+    return mDebugInfo;
+}
+
+void WindowInfosListenerInvoker::updateMaxSendDelay() {
+    if (!mDelayInfo) {
+        return;
     }
-
-    callListeners(shouldSyncDelayed);
+    nsecs_t delay = TimePoint::now().ns() - mDelayInfo->frameTime;
+    if (delay > mDebugInfo.maxSendDelayDuration) {
+        mDebugInfo.maxSendDelayDuration = delay;
+        mDebugInfo.maxSendDelayVsyncId = VsyncId{mDelayInfo->vsyncId};
+    }
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 701f11e..bc465a3 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -16,35 +16,48 @@
 
 #pragma once
 
+#include <optional>
+#include <unordered_set>
+
 #include <android/gui/BnWindowInfosReportedListener.h>
 #include <android/gui/IWindowInfosListener.h>
 #include <android/gui/IWindowInfosReportedListener.h>
 #include <binder/IBinder.h>
 #include <ftl/small_map.h>
+#include <gui/SpHash.h>
 #include <utils/Mutex.h>
 
+#include "scheduler/VsyncId.h"
+
 namespace android {
 
-class SurfaceFlinger;
+using WindowInfosReportedListenerSet =
+        std::unordered_set<sp<gui::IWindowInfosReportedListener>,
+                           gui::SpHash<gui::IWindowInfosReportedListener>>;
 
-class WindowInfosListenerInvoker : public IBinder::DeathRecipient {
+class WindowInfosListenerInvoker : public gui::BnWindowInfosReportedListener,
+                                   public IBinder::DeathRecipient {
 public:
-    explicit WindowInfosListenerInvoker(SurfaceFlinger&);
-
     void addWindowInfosListener(sp<gui::IWindowInfosListener>);
     void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
 
-    void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>,
-                            bool shouldSync, bool forceImmediateCall);
+    void windowInfosChanged(gui::WindowInfosUpdate update,
+                            WindowInfosReportedListenerSet windowInfosReportedListeners,
+                            bool forceImmediateCall);
+
+    binder::Status onWindowInfosReported() override;
+
+    struct DebugInfo {
+        VsyncId maxSendDelayVsyncId;
+        nsecs_t maxSendDelayDuration;
+        uint32_t pendingMessageCount;
+    };
+    DebugInfo getDebugInfo();
 
 protected:
     void binderDied(const wp<IBinder>& who) override;
 
 private:
-    struct WindowInfosReportedListener;
-    void windowInfosReported(bool shouldSync);
-
-    SurfaceFlinger& mFlinger;
     std::mutex mListenersMutex;
 
     static constexpr size_t kStaticCapacity = 3;
@@ -53,8 +66,16 @@
 
     std::mutex mMessagesMutex;
     uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0;
-    std::function<void(bool)> mWindowInfosChangedDelayed GUARDED_BY(mMessagesMutex);
-    bool mShouldSyncDelayed;
+    std::optional<gui::WindowInfosUpdate> mDelayedUpdate GUARDED_BY(mMessagesMutex);
+    WindowInfosReportedListenerSet mReportedListeners;
+
+    DebugInfo mDebugInfo GUARDED_BY(mMessagesMutex);
+    struct DelayInfo {
+        int64_t vsyncId;
+        nsecs_t frameTime;
+    };
+    std::optional<DelayInfo> mDelayInfo GUARDED_BY(mMessagesMutex);
+    void updateMaxSendDelay() REQUIRES(mMessagesMutex);
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp
index b0b6bf1..f76a8d7 100644
--- a/services/surfaceflinger/fuzzer/Android.bp
+++ b/services/surfaceflinger/fuzzer/Android.bp
@@ -69,6 +69,7 @@
         "-Wno-unused-result",
         "-Wno-conversion",
         "-Wno-sign-compare",
+        "-Wno-unused-function",
     ],
     fuzz_config: {
         cc: [
@@ -127,3 +128,13 @@
         "surfaceflinger_layer_fuzzer.cpp",
     ],
 }
+
+cc_fuzz {
+    name: "surfaceflinger_frametracer_fuzzer",
+    defaults: [
+        "surfaceflinger_fuzz_defaults",
+    ],
+    srcs: [
+        "surfaceflinger_frametracer_fuzzer.cpp",
+    ],
+}
diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md
index 78a7596..a06c41b 100644
--- a/services/surfaceflinger/fuzzer/README.md
+++ b/services/surfaceflinger/fuzzer/README.md
@@ -4,6 +4,7 @@
 + [DisplayHardware](#DisplayHardware)
 + [Scheduler](#Scheduler)
 + [Layer](#Layer)
++ [FrameTracer](#FrameTracer)
 
 # <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger
 
@@ -77,9 +78,8 @@
 Layer supports the following parameters:
 1. Display Connection Types (parameter name: `fakeDisplay`)
 2. State Sets (parameter name: `traverseInZOrder`)
-3. State Subsets (parameter name: `prepareCompositionState`)
-4. Disconnect modes (parameter name: `disconnect`)
-5. Data Spaces (parameter name: `setDataspace`)
+3. Disconnect modes (parameter name: `disconnect`)
+4. Data Spaces (parameter name: `setDataspace`)
 
 You can find the possible values in the fuzzer's source code.
 
@@ -93,3 +93,16 @@
   $ adb sync data
   $ adb shell /data/fuzz/arm64/surfaceflinger_layer_fuzzer/surfaceflinger_layer_fuzzer
 ```
+
+# <a name="FrameTracer"></a> Fuzzer for FrameTracer
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) surfaceflinger_frametracer_fuzzer
+```
+2. To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/surfaceflinger_frametracer_fuzzer/surfaceflinger_frametracer_fuzzer
+```
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index a605a2f..9fac14e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -116,7 +116,8 @@
 class DisplayHardwareFuzzer {
 public:
     DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
-        mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value();
+        mPhysicalDisplayId = TestableSurfaceFlinger::getFirstDisplayId().value_or(
+                PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint8_t>()));
     };
     void process();
 
@@ -221,7 +222,7 @@
     std::optional<impl::HWComposer::DeviceRequestedChanges> outChanges;
     mHwc.getDeviceCompositionChanges(halDisplayID,
                                      mFdp.ConsumeBool() /*frameUsesClientComposition*/,
-                                     std::chrono::steady_clock::now(), FenceTime::NO_FENCE,
+                                     std::chrono::steady_clock::now(),
                                      mFdp.ConsumeIntegral<nsecs_t>(), &outChanges);
 }
 
@@ -325,8 +326,7 @@
     invokeComposerHal2_3(&composer, display, outLayer);
     invokeComposerHal2_4(&composer, display, outLayer);
 
-    composer.executeCommands();
-    composer.resetCommands();
+    composer.executeCommands(display);
 
     composer.destroyLayer(display, outLayer);
     composer.destroyVirtualDisplay(display);
@@ -480,8 +480,8 @@
     BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
 
     sp<FramebufferSurface> surface =
-            new FramebufferSurface(mHwc, mPhysicalDisplayId, bqConsumer, getFuzzedSize() /*size*/,
-                                   getFuzzedSize() /*maxSize*/);
+            sp<FramebufferSurface>::make(mHwc, mPhysicalDisplayId, bqConsumer,
+                                         getFuzzedSize() /*size*/, getFuzzedSize() /*maxSize*/);
     surface->beginFrame(mFdp.ConsumeBool());
 
     surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes));
@@ -497,15 +497,15 @@
     DisplayIdGenerator<HalVirtualDisplayId> mGenerator;
     VirtualDisplayId VirtualDisplayId = mGenerator.generateId().value();
 
-    sp<SurfaceComposerClient> mClient = new SurfaceComposerClient();
+    sp<SurfaceComposerClient> mClient = sp<SurfaceComposerClient>::make();
     sp<SurfaceControl> mSurfaceControl =
             mClient->createSurface(String8("TestSurface"), 100, 100, PIXEL_FORMAT_RGBA_8888,
                                    ISurfaceComposerClient::eFXSurfaceBufferState,
                                    /*parent*/ nullptr);
 
-    sp<BLASTBufferQueue> mBlastBufferQueueAdapter =
-            new BLASTBufferQueue("TestBLASTBufferQueue", mSurfaceControl, 100, 100,
-                                 PIXEL_FORMAT_RGBA_8888);
+    auto mBlastBufferQueueAdapter =
+            sp<BLASTBufferQueue>::make("TestBLASTBufferQueue", mSurfaceControl, 100, 100,
+                                       PIXEL_FORMAT_RGBA_8888);
 
     sp<IGraphicBufferProducer> sink = mBlastBufferQueueAdapter->getIGraphicBufferProducer();
     sp<IGraphicBufferProducer> bqProducer = mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -513,9 +513,9 @@
     BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
     BufferQueue::createBufferQueue(&sink, &bqConsumer);
 
-    sp<VirtualDisplaySurface> surface =
-            new VirtualDisplaySurface(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer,
-                                      mFdp.ConsumeRandomLengthString().c_str() /*name*/);
+    auto surface =
+            sp<VirtualDisplaySurface>::make(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer,
+                                            mFdp.ConsumeRandomLengthString().c_str() /*name*/);
 
     surface->beginFrame(mFdp.ConsumeBool());
     surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes));
@@ -554,8 +554,7 @@
     mHwc.setClientTarget(halDisplayID, mFdp.ConsumeIntegral<uint32_t>(), Fence::NO_FENCE,
                          sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces));
 
-    mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now(),
-                                    FenceTime::NO_FENCE);
+    mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now());
 
     mHwc.setPowerMode(mPhysicalDisplayId, mFdp.PickValueInArray(kPowerModes));
 
@@ -565,7 +564,7 @@
 
     mHwc.getLayerReleaseFence(halDisplayID, layer);
 
-    mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make().get(), sp<GraphicBuffer>::make());
+    mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make(), sp<GraphicBuffer>::make());
 
     mHwc.clearReleaseFences(halDisplayID);
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
index 6a6e3db..1a951b3 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
@@ -41,6 +41,7 @@
 
 namespace android::hardware::graphics::composer::hal {
 
+using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::HWC2::ComposerCallback;
@@ -99,6 +100,7 @@
     void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {}
     void onComposerHalSeamlessPossible(HWDisplayId) {}
     void onComposerHalVsyncIdle(HWDisplayId) {}
+    void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) {}
 };
 
 } // namespace android::hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
new file mode 100644
index 0000000..a22a778
--- /dev/null
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_frametracer_fuzzer.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2021 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 <FrameTracer/FrameTracer.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <perfetto/trace/trace.pb.h>
+
+namespace android::fuzz {
+
+using namespace google::protobuf;
+
+constexpr size_t kMaxStringSize = 100;
+constexpr size_t kMinLayerIds = 1;
+constexpr size_t kMaxLayerIds = 10;
+constexpr int32_t kConfigDuration = 500;
+constexpr int32_t kBufferSize = 1024;
+constexpr int32_t kTimeOffset = 100000;
+
+class FrameTracerFuzzer {
+public:
+    FrameTracerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
+        // Fuzzer is single-threaded, so no need to be thread-safe.
+        static bool wasInitialized = false;
+        if (!wasInitialized) {
+            perfetto::TracingInitArgs args;
+            args.backends = perfetto::kInProcessBackend;
+            perfetto::Tracing::Initialize(args);
+            wasInitialized = true;
+        }
+        mFrameTracer = std::make_unique<android::FrameTracer>();
+    }
+    ~FrameTracerFuzzer() { mFrameTracer.reset(); }
+    void process();
+
+private:
+    std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest();
+    void traceTimestamp();
+    std::vector<int32_t> generateLayerIds(size_t numLayerIds);
+    void traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds);
+    void traceFence(std::vector<int32_t> layerIds, size_t numLayerIds);
+    std::unique_ptr<android::FrameTracer> mFrameTracer = nullptr;
+    FuzzedDataProvider mFdp;
+    android::FenceToFenceTimeMap mFenceFactory;
+};
+
+std::unique_ptr<perfetto::TracingSession> FrameTracerFuzzer::getTracingSessionForTest() {
+    perfetto::TraceConfig cfg;
+    cfg.set_duration_ms(kConfigDuration);
+    cfg.add_buffers()->set_size_kb(kBufferSize);
+    auto* dsCfg = cfg.add_data_sources()->mutable_config();
+    dsCfg->set_name(android::FrameTracer::kFrameTracerDataSource);
+
+    auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+    tracingSession->Setup(cfg);
+    return tracingSession;
+}
+
+std::vector<int32_t> FrameTracerFuzzer::generateLayerIds(size_t numLayerIds) {
+    std::vector<int32_t> layerIds;
+    for (size_t i = 0; i < numLayerIds; ++i) {
+        layerIds.push_back(mFdp.ConsumeIntegral<int32_t>());
+    }
+    return layerIds;
+}
+
+void FrameTracerFuzzer::traceTimestamp(std::vector<int32_t> layerIds, size_t numLayerIds) {
+    int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+    mFrameTracer->traceTimestamp(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/,
+                                 mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/,
+                                 mFdp.ConsumeIntegral<nsecs_t>() /*timestamp*/,
+                                 android::FrameTracer::FrameEvent::UNSPECIFIED,
+                                 mFdp.ConsumeIntegral<nsecs_t>() /*duration*/);
+}
+
+void FrameTracerFuzzer::traceFence(std::vector<int32_t> layerIds, size_t numLayerIds) {
+    const nsecs_t signalTime = systemTime();
+    const nsecs_t startTime = signalTime + kTimeOffset;
+    auto fence = mFenceFactory.createFenceTimeForTest(android::Fence::NO_FENCE);
+    mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, signalTime);
+    int32_t layerId = layerIds.at(mFdp.ConsumeIntegralInRange<size_t>(0, numLayerIds - 1));
+    mFrameTracer->traceFence(layerId, mFdp.ConsumeIntegral<uint64_t>() /*bufferID*/,
+                             mFdp.ConsumeIntegral<uint64_t>() /*frameNumber*/, fence,
+                             android::FrameTracer::FrameEvent::ACQUIRE_FENCE, startTime);
+}
+
+void FrameTracerFuzzer::process() {
+    mFrameTracer->registerDataSource();
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+
+    size_t numLayerIds = mFdp.ConsumeIntegralInRange<size_t>(kMinLayerIds, kMaxLayerIds);
+    std::vector<int32_t> layerIds = generateLayerIds(numLayerIds);
+
+    for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
+        mFrameTracer->traceNewLayer(*it /*layerId*/,
+                                    mFdp.ConsumeRandomLengthString(kMaxStringSize) /*layerName*/);
+    }
+
+    traceTimestamp(layerIds, numLayerIds);
+    traceFence(layerIds, numLayerIds);
+
+    mFenceFactory.signalAllForTest(android::Fence::NO_FENCE, systemTime());
+
+    tracingSession->StopBlocking();
+
+    for (auto it = layerIds.begin(); it != layerIds.end(); ++it) {
+        mFrameTracer->onDestroy(*it);
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FrameTracerFuzzer frameTracerFuzzer(data, size);
+    frameTracerFuzzer.process();
+    return 0;
+}
+
+} // namespace android::fuzz
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index f25043c..80943b5 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -103,7 +103,7 @@
 class SurfaceFlingerFuzzer {
 public:
     SurfaceFlingerFuzzer(const uint8_t *data, size_t size) : mFdp(data, size) {
-        mFlinger = mTestableFlinger.flinger();
+        mFlinger = sp<SurfaceFlinger>::fromExisting(mTestableFlinger.flinger());
     };
     void process(const uint8_t *data, size_t size);
 
@@ -130,7 +130,7 @@
     mFlinger->maxFrameBufferAcquiredBuffers = mFdp.ConsumeIntegral<int64_t>();
     mFlinger->maxGraphicsWidth = mFdp.ConsumeIntegral<uint32_t>();
     mFlinger->maxGraphicsHeight = mFdp.ConsumeIntegral<uint32_t>();
-    mFlinger->hasWideColorDisplay = mFdp.ConsumeBool();
+    mTestableFlinger.mutableSupportsWideColor() = mFdp.ConsumeBool();
     mFlinger->useContextPriority = mFdp.ConsumeBool();
 
     mFlinger->defaultCompositionDataspace = mFdp.PickValueInArray(kDataspaces);
@@ -150,8 +150,7 @@
 
     sp<IBinder> handle = defaultServiceManager()->checkService(
             String16(mFdp.ConsumeRandomLengthString().c_str()));
-    mFlinger->fromHandle(handle);
-    mFlinger->windowInfosReported();
+    LayerHandle::getLayer(handle);
     mFlinger->disableExpensiveRendering();
 }
 
@@ -186,11 +185,12 @@
     bool hasListenerCallbacks = mFdp.ConsumeBool();
     std::vector<ListenerCallbacks> listenerCallbacks{};
     uint64_t transactionId = mFdp.ConsumeIntegral<uint64_t>();
+    std::vector<uint64_t> mergedTransactionIds{};
 
     mTestableFlinger.setTransactionState(FrameTimelineInfo{}, states, displays, flags, applyToken,
                                          InputWindowCommands{}, desiredPresentTime, isAutoTimestamp,
-                                         {}, hasListenerCallbacks, listenerCallbacks,
-                                         transactionId);
+                                         {}, hasListenerCallbacks, listenerCallbacks, transactionId,
+                                         mergedTransactionIds);
 }
 
 void SurfaceFlingerFuzzer::setDisplayStateLocked() {
@@ -238,7 +238,8 @@
 
     mTestableFlinger.enableHalVirtualDisplays(mFdp.ConsumeBool());
 
-    mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>());
+    FTL_FAKE_GUARD(kMainThreadContext,
+                   mTestableFlinger.commitTransactionsLocked(mFdp.ConsumeIntegral<uint32_t>()));
 
     mTestableFlinger.notifyPowerBoost(mFdp.ConsumeIntegral<int32_t>());
 
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 867a198..4d03be0 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -30,19 +30,16 @@
 #include <ui/DisplayStatInfo.h>
 #include <ui/DynamicDisplayInfo.h>
 
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
-#include "ContainerLayer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/ComposerHal.h"
-#include "EffectLayer.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerHandle.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncConfiguration.h"
 #include "Scheduler/VsyncController.h"
@@ -50,9 +47,9 @@
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "ThreadContext.h"
 #include "TimeStats/TimeStats.h"
+#include "surfaceflinger_scheduler_fuzzer.h"
 
 #include "renderengine/mock/RenderEngine.h"
 #include "scheduler/TimeKeeper.h"
@@ -64,7 +61,6 @@
 #include "tests/unittests/mock/MockFrameTimeline.h"
 #include "tests/unittests/mock/MockFrameTracer.h"
 #include "tests/unittests/mock/MockNativeWindowSurface.h"
-#include "tests/unittests/mock/MockSurfaceInterceptor.h"
 #include "tests/unittests/mock/MockTimeStats.h"
 #include "tests/unittests/mock/MockVSyncTracker.h"
 #include "tests/unittests/mock/MockVsyncController.h"
@@ -86,7 +82,6 @@
 using types::V1_1::RenderIntent;
 using types::V1_2::ColorMode;
 using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
 using types::V1_2::PixelFormat;
 
 using V2_1::Config;
@@ -155,14 +150,26 @@
                                                     ui::PixelFormat::YCBCR_P010,
                                                     ui::PixelFormat::HSV_888};
 
-FloatRect getFuzzedFloatRect(FuzzedDataProvider *fdp) {
+inline VsyncId getFuzzedVsyncId(FuzzedDataProvider& fdp) {
+    return VsyncId{fdp.ConsumeIntegral<int64_t>()};
+}
+
+inline TimePoint getFuzzedTimePoint(FuzzedDataProvider& fdp) {
+    return TimePoint::fromNs(fdp.ConsumeIntegral<nsecs_t>());
+}
+
+inline Duration getFuzzedDuration(FuzzedDataProvider& fdp) {
+    return Duration::fromNs(fdp.ConsumeIntegral<nsecs_t>());
+}
+
+inline FloatRect getFuzzedFloatRect(FuzzedDataProvider* fdp) {
     return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/,
                      fdp->ConsumeFloatingPoint<float>() /*right*/,
                      fdp->ConsumeFloatingPoint<float>() /*top*/,
                      fdp->ConsumeFloatingPoint<float>() /*bottom*/);
 }
 
-HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider *fdp) {
+inline HdrMetadata getFuzzedHdrMetadata(FuzzedDataProvider* fdp) {
     HdrMetadata hdrMetadata;
     if (fdp->ConsumeBool()) {
         hdrMetadata.cta8613.maxContentLightLevel = fdp->ConsumeFloatingPoint<float>();
@@ -194,9 +201,11 @@
     static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
     static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
 
-    VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); }
+    scheduler::VsyncConfigSet getConfigsForRefreshRate(Fps) const override {
+        return getCurrentConfigs();
+    }
 
-    VsyncConfigSet getCurrentConfigs() const override {
+    scheduler::VsyncConfigSet getCurrentConfigs() const override {
         return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
                  FAKE_DURATION_OFFSET_NS},
                 {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
@@ -215,30 +224,32 @@
 
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs> &refreshRateConfigs,
-                      ISchedulerCallback &callback)
+    TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<android::mock::VsyncController>(),
-                              std::make_unique<android::mock::VSyncTracker>(), refreshRateConfigs,
-                              callback) {}
+                              std::make_shared<android::mock::VSyncTracker>(), selectorPtr,
+                              std::move(modulatorPtr), callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
-                      std::unique_ptr<VSyncTracker> tracker,
-                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback &callback)
-          : Scheduler(*this, callback, Feature::kContentDetection) {
-        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateConfigs(std::move(configs));
+                      VsyncSchedule::TrackerPtr tracker,
+                      std::shared_ptr<RefreshRateSelector> selectorPtr,
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+        const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
+        registerDisplayInternal(displayId, std::move(selectorPtr),
+                                std::shared_ptr<VsyncSchedule>(
+                                        new VsyncSchedule(displayId, std::move(tracker),
+                                                          std::make_shared<FuzzImplVSyncDispatch>(),
+                                                          std::move(controller))));
     }
 
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
         return Scheduler::createConnection(std::move(eventThread));
     }
 
-    auto &mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
-    auto &mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
-
     auto &mutableLayerHistory() { return mLayerHistory; }
 
-    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
+    auto refreshRateSelector() { return pacesetterSelectorPtr(); }
 
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
@@ -266,14 +277,17 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode &mode) {
         return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
+    using Scheduler::setVsyncConfig;
+
 private:
     // ICompositor overrides:
-    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t, int64_t) override {}
+    void configure() override {}
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 
     // MessageQueue overrides:
@@ -286,13 +300,18 @@
 namespace surfaceflinger::test {
 
 class Factory final : public surfaceflinger::Factory {
+    struct NoOpMessageQueue : android::impl::MessageQueue {
+        using android::impl::MessageQueue::MessageQueue;
+        void onFrameSignal(ICompositor&, VsyncId, TimePoint) override {}
+    };
+
 public:
     ~Factory() = default;
 
-    std::unique_ptr<HWComposer> createHWComposer(const std::string &) override { return nullptr; }
+    std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
 
-    std::unique_ptr<MessageQueue> createMessageQueue(ICompositor &compositor) {
-        return std::make_unique<android::impl::MessageQueue>(compositor);
+    std::unique_ptr<MessageQueue> createMessageQueue(ICompositor& compositor) {
+        return std::make_unique<NoOpMessageQueue>(compositor);
     }
 
     std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
@@ -301,27 +320,23 @@
     }
 
     std::unique_ptr<scheduler::Scheduler> createScheduler(
-            const std::shared_ptr<scheduler::RefreshRateConfigs> &,
-            scheduler::ISchedulerCallback &) {
+            const std::shared_ptr<scheduler::RefreshRateSelector>&,
+            scheduler::ISchedulerCallback&) {
         return nullptr;
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return new android::impl::SurfaceInterceptor();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
-        return new StartPropertySetThread(timestampPropertyValue);
+        return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
 
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs &creationArgs) override {
-        return new DisplayDevice(creationArgs);
+        return sp<DisplayDevice>::make(creationArgs);
     }
 
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
                                           uint32_t layerCount, uint64_t usage,
                                           std::string requestorName) override {
-        return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+        return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName);
     }
 
     void createBufferQueue(sp<IGraphicBufferProducer> *outProducer,
@@ -334,18 +349,6 @@
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer> &producer,
-                                                       const sp<SurfaceFlinger> &flinger,
-                                                       const wp<Layer> &layer) override {
-        return new MonitoredProducer(producer, flinger, layer);
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer> &consumer,
-                                                      renderengine::RenderEngine &renderEngine,
-                                                      uint32_t textureName, Layer *layer) override {
-        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-    }
-
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer> &producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -356,20 +359,14 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs &) override {
-        return nullptr;
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs &) override { return nullptr; }
+
+    sp<Layer> createEffectLayer(const LayerCreationArgs &args) override {
+        return sp<Layer>::make(args);
     }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs &) override {
-        return nullptr;
-    }
-
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs &args) override {
-        return new EffectLayer(args);
-    }
-
-    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs &args) override {
-        return new ContainerLayer(args);
+    sp<LayerFE> createLayerFE(const std::string &layerName) override {
+        return sp<LayerFE>::make(layerName);
     }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
@@ -407,9 +404,8 @@
     SurfaceFlinger *flinger() { return mFlinger.get(); }
     scheduler::TestableScheduler *scheduler() { return mScheduler; }
 
-    // Allow reading display state without locking, as if called on the SF main thread.
-    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->onInitializeDisplays();
+    void initializeDisplays() {
+        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->initializeDisplays());
     }
 
     void setGlobalShadowSettings(FuzzedDataProvider *fdp) {
@@ -430,7 +426,7 @@
 
     void onPullAtom(FuzzedDataProvider *fdp) {
         const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
-        std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+        std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
         bool success = fdp->ConsumeBool();
         mFlinger->onPullAtom(atomId, &pulledData, &success);
     }
@@ -447,21 +443,18 @@
         mFlinger->clearStatsLocked(dumpArgs, result);
 
         mFlinger->dumpTimeStats(dumpArgs, fdp->ConsumeBool(), result);
-        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->logFrameStats());
+        FTL_FAKE_GUARD(kMainThreadContext,
+                       mFlinger->logFrameStats(TimePoint::fromNs(fdp->ConsumeIntegral<nsecs_t>())));
 
         result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpFrameTimeline(dumpArgs, result);
 
         result = fdp->ConsumeRandomLengthString().c_str();
-        mFlinger->dumpStaticScreenStats(result);
-
-        result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result);
 
         LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
         mFlinger->dumpOffscreenLayersProto(layersProto);
-        LayersTraceProto layersTraceProto{};
-        mFlinger->dumpDisplayProto(layersTraceProto);
+        mFlinger->dumpDisplayProto();
 
         result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpHwc(result);
@@ -469,10 +462,6 @@
         mFlinger->calculateColorMatrix(fdp->ConsumeFloatingPoint<float>());
         mFlinger->updateColorMatrixLocked();
         mFlinger->CheckTransactCodeCredentials(fdp->ConsumeIntegral<uint32_t>());
-
-        const CountDownLatch transactionCommittedSignal(fdp->ConsumeIntegral<uint32_t>());
-        mFlinger->waitForSynchronousTransaction(transactionCommittedSignal);
-        mFlinger->signalSynchronousTransactions(fdp->ConsumeIntegral<uint32_t>());
     }
 
     void getCompositionPreference() {
@@ -508,14 +497,14 @@
         mFlinger->getDisplayState(display, &displayState);
     }
 
-    void getStaticDisplayInfo(sp<IBinder> &display) {
+    void getStaticDisplayInfo(int64_t displayId) {
         ui::StaticDisplayInfo staticDisplayInfo;
-        mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+        mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
     }
 
-    void getDynamicDisplayInfo(sp<IBinder> &display) {
+    void getDynamicDisplayInfo(int64_t displayId) {
         android::ui::DynamicDisplayInfo dynamicDisplayInfo;
-        mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+        mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
     }
     void getDisplayNativePrimaries(sp<IBinder> &display) {
         android::ui::DisplayPrimaries displayPrimaries;
@@ -523,36 +512,20 @@
     }
 
     void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
-        ui::DisplayModeId outDefaultMode;
-        bool outAllowGroupSwitching;
-        float outPrimaryRefreshRateMin;
-        float outPrimaryRefreshRateMax;
-        float outAppRequestRefreshRateMin;
-        float outAppRequestRefreshRateMax;
-        mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching,
-                                             &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax,
-                                             &outAppRequestRefreshRateMin,
-                                             &outAppRequestRefreshRateMax);
+        gui::DisplayModeSpecs _;
+        mFlinger->getDesiredDisplayModeSpecs(display, &_);
     }
 
-    void setVsyncConfig(FuzzedDataProvider *fdp) {
-        const scheduler::VsyncModulator::VsyncConfig vsyncConfig{};
-        mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
+    // TODO(b/248317436): extend to cover all displays for multi-display devices
+    static std::optional<PhysicalDisplayId> getFirstDisplayId() {
+        std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) return {};
+        return ids.front();
     }
 
-    void updateCompositorTiming(FuzzedDataProvider *fdp) {
-        std::shared_ptr<FenceTime> presentFenceTime = FenceTime::NO_FENCE;
-        mFlinger->updateCompositorTiming({}, fdp->ConsumeIntegral<nsecs_t>(), presentFenceTime);
-    }
-
-    void getCompositorTiming() {
-        CompositorTiming compositorTiming;
-        mFlinger->getCompositorTiming(&compositorTiming);
-    }
-
-    sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+    std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
         mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
-        mFlinger->createConnection();
+        const sp<Client> client = sp<Client>::make(mFlinger);
 
         DisplayIdGenerator<HalVirtualDisplayId> kGenerator;
         HalVirtualDisplayId halVirtualDisplayId = kGenerator.generateId().value();
@@ -561,14 +534,15 @@
         ui::PixelFormat pixelFormat{};
         mFlinger->getHwComposer().allocateVirtualDisplay(halVirtualDisplayId, uiSize, &pixelFormat);
 
-        PhysicalDisplayId physicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value();
+        PhysicalDisplayId physicalDisplayId = getFirstDisplayId().value_or(
+                PhysicalDisplayId::fromPort(fdp->ConsumeIntegral<uint8_t>()));
         mFlinger->getHwComposer().allocatePhysicalDisplay(kHwDisplayId, physicalDisplayId);
 
         sp<IBinder> display =
                 mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()),
                                         fdp->ConsumeBool());
 
-        onInitializeDisplays();
+        initializeDisplays();
         mFlinger->getPhysicalDisplayToken(physicalDisplayId);
 
         mFlinger->mStartPropertySetThread =
@@ -576,36 +550,32 @@
 
         mFlinger->bootFinished();
 
-        return display;
+        return {display, physicalDisplayId.value};
     }
 
     void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
         FuzzedDataProvider mFdp(data, size);
 
-        sp<IBinder> display = fuzzBoot(&mFdp);
+        auto [display, displayId] = fuzzBoot(&mFdp);
 
         sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
-        mFlinger->authenticateSurfaceTexture(bufferProducer.get());
 
         mFlinger->createDisplayEventConnection();
 
         getDisplayStats(display);
         getDisplayState(display);
-        getStaticDisplayInfo(display);
-        getDynamicDisplayInfo(display);
+        getStaticDisplayInfo(displayId);
+        getDynamicDisplayInfo(displayId);
         getDisplayNativePrimaries(display);
 
         mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
         mFlinger->setGameContentType(display, mFdp.ConsumeBool());
         mFlinger->setPowerMode(display, mFdp.ConsumeIntegral<int>());
-        mFlinger->clearAnimationFrameStats();
 
         overrideHdrTypes(display, &mFdp);
 
         onPullAtom(&mFdp);
 
-        mFlinger->injectVSync(mFdp.ConsumeIntegral<nsecs_t>());
-
         getCompositionPreference();
         getDisplayedContentSample(display, &mFdp);
         getDesiredDisplayModeSpecs(display);
@@ -620,19 +590,28 @@
         mFlinger->binderDied(display);
         mFlinger->onFirstRef();
 
-        mFlinger->commitTransactions();
-        mFlinger->updateInputFlinger();
+        mFlinger->updateInputFlinger(VsyncId{}, TimePoint{});
         mFlinger->updateCursorAsync();
 
-        setVsyncConfig(&mFdp);
+        mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+                                           .appOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+                                           .sfWorkDuration = getFuzzedDuration(mFdp),
+                                           .appWorkDuration = getFuzzedDuration(mFdp)},
+                                          getFuzzedDuration(mFdp));
 
-        mFlinger->flushTransactionQueues(0);
+        {
+            ftl::FakeGuard guard(kMainThreadContext);
+
+            mFlinger->commitTransactions();
+            mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
+            mFlinger->postComposition(systemTime());
+        }
 
         mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
         mFlinger->clearTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
         mFlinger->commitOffscreenLayers();
 
-        mFlinger->frameIsEarly(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeIntegral<int64_t>());
+        mFlinger->frameIsEarly(getFuzzedTimePoint(mFdp), getFuzzedVsyncId(mFdp));
         mFlinger->computeLayerBounds();
         mFlinger->startBootAnim();
 
@@ -643,14 +622,6 @@
 
         mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mFdp.ConsumeIntegral<uid_t>());
 
-        mFlinger->postComposition();
-
-        getCompositorTiming();
-
-        updateCompositorTiming(&mFdp);
-
-        mFlinger->setCompositorTimingSnapped({}, mFdp.ConsumeIntegral<nsecs_t>());
-        FTL_FAKE_GUARD(kMainThreadContext, mFlinger->postFrame());
         mFlinger->calculateExpectedPresentTime({});
 
         mFlinger->enableHalVirtualDisplays(mFdp.ConsumeBool());
@@ -661,7 +632,8 @@
     }
 
     void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+        mFlinger->mRenderEngine = std::move(renderEngine);
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -675,10 +647,10 @@
 
     // The ISchedulerCallback argument can be nullptr for a no-op implementation.
     void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
-                        std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+                        std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
-                        scheduler::ISchedulerCallback *callback = nullptr,
+                        scheduler::ISchedulerCallback* callback = nullptr,
                         bool hasMultipleModes = false) {
         constexpr DisplayModeId kModeId60{0};
         DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
@@ -688,18 +660,20 @@
             modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
         }
 
-        mRefreshRateConfigs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-        const auto fps = mRefreshRateConfigs->getActiveMode()->getFps();
+        mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
+        const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
-                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
                                                               hal::PowerMode::OFF);
 
+        auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
-                                                      std::move(vsyncTracker), mRefreshRateConfigs,
-                                                      *(callback ?: this));
+                                                      std::move(vsyncTracker), mRefreshRateSelector,
+                                                      std::move(modulatorPtr), *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -740,9 +714,9 @@
 
     void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
 
-    auto commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags) FTL_FAKE_GUARD(kMainThreadContext) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        return mFlinger->commitTransactionsLocked(transactionFlags);
+        mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     auto setDisplayStateLocked(const DisplayState &s) {
@@ -758,28 +732,35 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto &getTransactionQueue() { return mFlinger->mTransactionQueue; }
-    auto &getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
-
-    auto setTransactionState(
-            const FrameTimelineInfo &frameTimelineInfo, const Vector<ComposerState> &states,
-            const Vector<DisplayState> &displays, uint32_t flags, const sp<IBinder> &applyToken,
-            const InputWindowCommands &inputWindowCommands, int64_t desiredPresentTime,
-            bool isAutoTimestamp, const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
-            std::vector<ListenerCallbacks> &listenerCallbacks, uint64_t transactionId) {
-        return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
-                                             inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
-                                             listenerCallbacks, transactionId);
+    auto &getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto &getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
     }
 
-    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
+    auto setTransactionState(
+            const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+            bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
+            uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
+        return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
+                                             inputWindowCommands, desiredPresentTime,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
+                                             listenerCallbacks, transactionId,
+                                             mergedTransactionIds);
+    }
+
+    auto flushTransactionQueues() {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return mFlinger->flushTransactionQueues(VsyncId{0});
+    }
 
     auto onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
         return mFlinger->onTransact(code, data, reply, flags);
     }
 
-    auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
+    auto getGpuContextPriority() { return mFlinger->getGpuContextPriority(); }
 
     auto calculateMaxAcquiredBufferCount(Fps refreshRate,
                                          std::chrono::nanoseconds presentLatency) const {
@@ -789,35 +770,34 @@
     /* Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
+    auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
+    auto& mutableCurrentState() { return mFlinger->mCurrentState; }
+    auto& mutableDisplays() { return mFlinger->mDisplays; }
+    auto& mutableDrawingState() { return mFlinger->mDrawingState; }
 
-    auto &mutableCurrentState() { return mFlinger->mCurrentState; }
-    auto &mutableDisplays() { return mFlinger->mDisplays; }
-    auto &mutableDrawingState() { return mFlinger->mDrawingState; }
-    auto &mutableInterceptor() { return mFlinger->mInterceptor; }
-
-    auto fromHandle(const sp<IBinder> &handle) { return mFlinger->fromHandle(handle); }
+    auto fromHandle(const sp<IBinder> &handle) { return LayerHandle::getLayer(handle); }
 
     ~TestableSurfaceFlinger() {
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
-        mFlinger->mCompositionEngine->setRenderEngine(
-                std::unique_ptr<renderengine::RenderEngine>());
+        mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
 private:
-    void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void setVsyncEnabled(PhysicalDisplayId, bool) override {}
+    void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 
     surfaceflinger::test::Factory mFactory;
-    sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
+    sp<SurfaceFlinger> mFlinger =
+            sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
     scheduler::TestableScheduler *mScheduler = nullptr;
-    std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index 34cf906..921cae4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -14,13 +14,9 @@
  * limitations under the License.
  *
  */
-#include <BufferStateLayer.h>
 #include <Client.h>
 #include <DisplayDevice.h>
-#include <EffectLayer.h>
-#include <LayerRejecter.h>
 #include <LayerRenderArea.h>
-#include <MonitoredProducer.h>
 #include <ftl/future.h>
 #include <fuzzer/FuzzedDataProvider.h>
 #include <gui/IProducerListener.h>
@@ -29,6 +25,7 @@
 #include <gui/WindowInfo.h>
 #include <renderengine/mock/FakeExternalTexture.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/Transform.h>
 
 #include <FuzzableDataspaces.h>
 #include <surfaceflinger_fuzzers_utils.h>
@@ -46,6 +43,7 @@
     void invokeEffectLayer();
     LayerCreationArgs createLayerCreationArgs(TestableSurfaceFlinger* flinger, sp<Client> client);
     Rect getFuzzedRect();
+    ui::Transform getFuzzedTransform();
     FrameTimelineInfo getFuzzedFrameTimelineInfo();
 
 private:
@@ -58,9 +56,17 @@
                 mFdp.ConsumeIntegral<int32_t>() /*bottom*/);
 }
 
+ui::Transform LayerFuzzer::getFuzzedTransform() {
+    return ui::Transform(mFdp.ConsumeIntegral<int32_t>() /*orientation*/,
+                         mFdp.ConsumeIntegral<int32_t>() /*width*/,
+                         mFdp.ConsumeIntegral<int32_t>() /*height*/);
+}
+
 FrameTimelineInfo LayerFuzzer::getFuzzedFrameTimelineInfo() {
-    return FrameTimelineInfo{.vsyncId = mFdp.ConsumeIntegral<int64_t>(),
-                             .inputEventId = mFdp.ConsumeIntegral<int32_t>()};
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = mFdp.ConsumeIntegral<int64_t>();
+    ftInfo.inputEventId = mFdp.ConsumeIntegral<int32_t>();
+    return ftInfo;
 }
 
 LayerCreationArgs LayerFuzzer::createLayerCreationArgs(TestableSurfaceFlinger* flinger,
@@ -77,15 +83,15 @@
 
 void LayerFuzzer::invokeEffectLayer() {
     TestableSurfaceFlinger flinger;
-    sp<Client> client = sp<Client>::make(flinger.flinger());
+    sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
     const LayerCreationArgs layerCreationArgs = createLayerCreationArgs(&flinger, client);
-    sp<EffectLayer> effectLayer = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> effectLayer = sp<Layer>::make(layerCreationArgs);
 
     effectLayer->setColor({(mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*x*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*y*/,
                             mFdp.ConsumeFloatingPointInRange<float>(0, 255) /*z*/)});
     effectLayer->setDataspace(mFdp.PickValueInArray(kDataspaces));
-    sp<EffectLayer> parent = sp<EffectLayer>::make(layerCreationArgs);
+    sp<Layer> parent = sp<Layer>::make(layerCreationArgs);
     effectLayer->setChildrenDrawingParent(parent);
 
     const FrameTimelineInfo frameInfo = getFuzzedFrameTimelineInfo();
@@ -109,24 +115,25 @@
 
 void LayerFuzzer::invokeBufferStateLayer() {
     TestableSurfaceFlinger flinger;
-    sp<Client> client = sp<Client>::make(flinger.flinger());
-    sp<BufferStateLayer> layer =
-            sp<BufferStateLayer>::make(createLayerCreationArgs(&flinger, client));
+    sp<Client> client = sp<Client>::make(sp<SurfaceFlinger>::fromExisting(flinger.flinger()));
+    sp<Layer> layer = sp<Layer>::make(createLayerCreationArgs(&flinger, client));
     sp<Fence> fence = sp<Fence>::make();
     const std::shared_ptr<FenceTime> fenceTime = std::make_shared<FenceTime>(fence);
 
-    const CompositorTiming compositor = {mFdp.ConsumeIntegral<int64_t>(),
-                                         mFdp.ConsumeIntegral<int64_t>(),
-                                         mFdp.ConsumeIntegral<int64_t>()};
+    const CompositorTiming compositorTiming(mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>(),
+                                            mFdp.ConsumeIntegral<int64_t>());
 
-    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share());
-    layer->onLayerDisplayed(
-            ftl::yield<FenceResult>(base::unexpected(mFdp.ConsumeIntegral<status_t>())).share());
+    layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+                            ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
+    layer->onLayerDisplayed(ftl::yield<FenceResult>(
+                                    base::unexpected(mFdp.ConsumeIntegral<status_t>()))
+                                    .share(),
+                            ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>()));
 
     layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>());
-    layer->finalizeFrameEventHistory(fenceTime, compositor);
-    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositor);
-    layer->isBufferDue(mFdp.ConsumeIntegral<int64_t>());
+    layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming);
 
     layer->setTransform(mFdp.ConsumeIntegral<uint32_t>());
     layer->setTransformToDisplayInverse(mFdp.ConsumeBool());
@@ -150,10 +157,9 @@
     layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
 
     layer->fenceHasSignaled();
-    layer->framePresentTimeIsCurrent(mFdp.ConsumeIntegral<int64_t>());
     layer->onPreComposition(mFdp.ConsumeIntegral<int64_t>());
     const std::vector<sp<CallbackHandle>> callbacks;
-    layer->setTransactionCompletedListeners(callbacks);
+    layer->setTransactionCompletedListeners(callbacks, mFdp.ConsumeBool());
 
     std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
             renderengine::mock::FakeExternalTexture>(mFdp.ConsumeIntegral<uint32_t>(),
@@ -171,7 +177,8 @@
                               {mFdp.ConsumeIntegral<int32_t>(),
                                mFdp.ConsumeIntegral<int32_t>()} /*reqSize*/,
                               mFdp.PickValueInArray(kDataspaces), mFdp.ConsumeBool(),
-                              getFuzzedRect(), mFdp.ConsumeBool());
+                              mFdp.ConsumeBool(), getFuzzedTransform(), getFuzzedRect(),
+                              mFdp.ConsumeBool());
     layerArea.render([]() {} /*drawLayers*/);
 
     if (!ownsHandle) {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index da60a69..f17d2e1 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -19,12 +19,17 @@
 #include <fuzzer/FuzzedDataProvider.h>
 #include <processgroup/sched_policy.h>
 
-#include "Scheduler/DispSyncSource.h"
+#include <scheduler/PresentLatencyTracker.h>
+
 #include "Scheduler/OneShotTimer.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "Scheduler/VSyncDispatchTimerQueue.h"
 #include "Scheduler/VSyncPredictor.h"
 #include "Scheduler/VSyncReactor.h"
 
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+
 #include "surfaceflinger_fuzzers_utils.h"
 #include "surfaceflinger_scheduler_fuzzer.h"
 
@@ -36,13 +41,14 @@
                                      (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(),
                                      (120_Hz).getPeriodNsecs()};
 
-constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateConfigs::LayerVoteType>();
+constexpr auto kLayerVoteTypes = ftl::enum_range<scheduler::RefreshRateSelector::LayerVoteType>();
 
 constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF,
                                      PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND};
 
 constexpr uint16_t kRandomStringLength = 256;
 constexpr std::chrono::duration kSyncPeriod(16ms);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
 
 template <typename T>
 void dump(T* component, FuzzedDataProvider* fdp) {
@@ -50,19 +56,19 @@
     component->dump(res);
 }
 
-class SchedulerFuzzer : private VSyncSource::Callback {
+class SchedulerFuzzer {
 public:
     SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
     void process();
 
 private:
     void fuzzRefreshRateSelection();
-    void fuzzRefreshRateConfigs();
+    void fuzzRefreshRateSelector();
+    void fuzzPresentLatencyTracker();
     void fuzzVSyncModulator();
     void fuzzVSyncPredictor();
     void fuzzVSyncReactor();
     void fuzzLayerHistory();
-    void fuzzDispSyncSource();
     void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch);
     void fuzzVSyncDispatchTimerQueue();
     void fuzzOneShotTimer();
@@ -71,8 +77,7 @@
 
     FuzzedDataProvider mFdp;
 
-protected:
-    void onVSyncEvent(nsecs_t /* when */, VSyncSource::VSyncData) {}
+    std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
 };
 
 PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() {
@@ -86,44 +91,30 @@
 }
 
 void SchedulerFuzzer::fuzzEventThread() {
+    mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
+            new scheduler::VsyncSchedule(getPhysicalDisplayId(),
+                                         std::make_shared<mock::VSyncTracker>(),
+                                         std::make_shared<mock::VSyncDispatch>(), nullptr));
     const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); };
     std::unique_ptr<android::impl::EventThread> thread = std::make_unique<
-            android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr,
-                                        nullptr, nullptr, getVsyncPeriod);
+            android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod,
+                                        (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
+                                        (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
 
     thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool());
     sp<EventThreadConnection> connection =
-            new EventThreadConnection(thread.get(), mFdp.ConsumeIntegral<uint16_t>(), nullptr,
-                                      {} /*eventRegistration*/);
+            sp<EventThreadConnection>::make(thread.get(), mFdp.ConsumeIntegral<uint16_t>(),
+                                            nullptr);
     thread->requestNextVsync(connection);
     thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection);
 
     thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
                         (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
     thread->registerDisplayEventConnection(connection);
-    thread->onScreenAcquired();
-    thread->onScreenReleased();
+    thread->enableSyntheticVsync(mFdp.ConsumeBool());
     dump<android::impl::EventThread>(thread.get(), &mFdp);
 }
 
-void SchedulerFuzzer::fuzzDispSyncSource() {
-    std::unique_ptr<FuzzImplVSyncDispatch> vSyncDispatch =
-            std::make_unique<FuzzImplVSyncDispatch>();
-    std::unique_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_unique<FuzzImplVSyncTracker>();
-    std::unique_ptr<scheduler::DispSyncSource> dispSyncSource = std::make_unique<
-            scheduler::DispSyncSource>(*vSyncDispatch, *vSyncTracker,
-                                       (std::chrono::nanoseconds)
-                                               mFdp.ConsumeIntegral<uint64_t>() /*workDuration*/,
-                                       (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()
-                                       /*readyDuration*/,
-                                       mFdp.ConsumeBool(),
-                                       mFdp.ConsumeRandomLengthString(kRandomStringLength).c_str());
-    dispSyncSource->setVSyncEnabled(true);
-    dispSyncSource->setCallback(this);
-    dispSyncSource->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), 0ns);
-    dump<scheduler::DispSyncSource>(dispSyncSource.get(), &mFdp);
-}
-
 void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) {
     scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback(
             [&](auto, auto, auto) {
@@ -142,7 +133,7 @@
 }
 
 void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() {
-    FuzzImplVSyncTracker stubTracker{mFdp.ConsumeIntegral<nsecs_t>()};
+    auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>());
     scheduler::VSyncDispatchTimerQueue
             mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker,
                       mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/,
@@ -155,17 +146,17 @@
     scheduler::VSyncDispatchTimerQueueEntry entry(
             "fuzz", [](auto, auto, auto) {},
             mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/);
-    entry.update(stubTracker, 0);
+    entry.update(*stubTracker, 0);
     entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
                     .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
                     .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
-                   stubTracker, 0);
+                   *stubTracker, 0);
     entry.disarm();
     entry.ensureNotRunning();
     entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(),
                     .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(),
                     .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()},
-                   stubTracker, 0);
+                   *stubTracker, 0);
     auto const wakeup = entry.wakeupTime();
     auto const ready = entry.readyTime();
     entry.callback(entry.executing(), *wakeup, *ready);
@@ -179,7 +170,8 @@
     uint16_t now = mFdp.ConsumeIntegral<uint16_t>();
     uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
     uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX);
-    scheduler::VSyncPredictor tracker{mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
+    scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID,
+                                      mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize,
                                       minimumSamplesForPrediction,
                                       mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/};
     uint16_t period = mFdp.ConsumeIntegral<uint16_t>();
@@ -224,19 +216,19 @@
     nsecs_t time2 = time1;
     uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>();
 
-    sp<FuzzImplLayer> layer1 = new FuzzImplLayer(flinger.flinger());
-    sp<FuzzImplLayer> layer2 = new FuzzImplLayer(flinger.flinger());
+    sp<FuzzImplLayer> layer1 = sp<FuzzImplLayer>::make(flinger.flinger());
+    sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger());
 
     for (int i = 0; i < historySize; ++i) {
-        historyV1.record(layer1.get(), time1, time1,
+        historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1,
                          scheduler::LayerHistory::LayerUpdateType::Buffer);
-        historyV1.record(layer2.get(), time2, time2,
+        historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2,
                          scheduler::LayerHistory::LayerUpdateType::Buffer);
         time1 += mFdp.PickValueInArray(kVsyncPeriods);
         time2 += mFdp.PickValueInArray(kVsyncPeriods);
     }
-    historyV1.summarize(*scheduler->refreshRateConfigs(), time1);
-    historyV1.summarize(*scheduler->refreshRateConfigs(), time2);
+    historyV1.summarize(*scheduler->refreshRateSelector(), time1);
+    historyV1.summarize(*scheduler->refreshRateSelector(), time2);
 
     scheduler->createConnection(std::make_unique<android::mock::EventThread>());
 
@@ -245,22 +237,26 @@
     scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(),
                            (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>());
 
-    dump<scheduler::TestableScheduler>(scheduler, &mFdp);
+    std::string result = mFdp.ConsumeRandomLengthString(kRandomStringLength);
+    utils::Dumper dumper(result);
+    scheduler->dump(dumper);
 }
 
 void SchedulerFuzzer::fuzzVSyncReactor() {
     std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>();
-    scheduler::VSyncReactor reactor(std::make_unique<ClockWrapper>(
+    scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID,
+                                    std::make_unique<ClockWrapper>(
                                             std::make_shared<FuzzImplClock>()),
                                     *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/,
                                     false);
 
-    reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>());
-    bool periodFlushed = mFdp.ConsumeBool();
+    reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool());
+    bool periodFlushed = false; // Value does not matter, since this is an out
+                                // param from addHwVsyncTimestamp.
     reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed);
     reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt,
                                 &periodFlushed);
-    sp<Fence> fence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING));
+    sp<Fence> fence = sp<Fence>::make(memfd_create("fd", MFD_ALLOW_SEALING));
     std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
     vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>());
     FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>());
@@ -288,17 +284,14 @@
     };
     using Schedule = scheduler::TransactionSchedule;
     using nanos = std::chrono::nanoseconds;
-    using VsyncModulator = scheduler::VsyncModulator;
     using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
-    const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
-                                            nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
-    const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
-                                               nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
-    const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE,
-                                           nanos(SF_DURATION_EARLY_GPU),
-                                           nanos(APP_DURATION_EARLY_GPU)};
-    const VsyncModulator::VsyncConfigSet offsets = {early, earlyGpu, late,
-                                                    nanos(HWC_MIN_WORK_DURATION)};
+    const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+                                       nanos(APP_DURATION_LATE)};
+    const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+                                          nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
+    const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+                                      nanos(APP_DURATION_EARLY_GPU)};
+    const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)};
     sp<FuzzImplVsyncModulator> vSyncModulator =
             sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now);
     (void)vSyncModulator->setVsyncConfigSet(offsets);
@@ -319,14 +312,14 @@
     LayerCreationArgs args(flinger.flinger(), client,
                            mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/,
                            mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata());
-    sp<Layer> layer = new BufferQueueLayer(args);
+    sp<Layer> layer = sp<Layer>::make(args);
 
     layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>());
 }
 
-void SchedulerFuzzer::fuzzRefreshRateConfigs() {
-    using RefreshRateConfigs = scheduler::RefreshRateConfigs;
-    using LayerRequirement = RefreshRateConfigs::LayerRequirement;
+void SchedulerFuzzer::fuzzRefreshRateSelector() {
+    using RefreshRateSelector = scheduler::RefreshRateSelector;
+    using LayerRequirement = RefreshRateSelector::LayerRequirement;
     using RefreshRateStats = scheduler::RefreshRateStats;
 
     const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1);
@@ -342,54 +335,75 @@
                                                          Fps::fromValue(static_cast<float>(fps))));
     }
 
-    RefreshRateConfigs refreshRateConfigs(displayModes, modeId);
+    RefreshRateSelector refreshRateSelector(displayModes, modeId);
 
-    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
     std::vector<LayerRequirement> layers = {{.weight = mFdp.ConsumeFloatingPoint<float>()}};
 
-    refreshRateConfigs.getBestRefreshRate(layers, globalSignals);
+    refreshRateSelector.getRankedFrameRates(layers, globalSignals);
 
     layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength);
     layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>();
     layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>());
     layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values);
     auto frameRateOverrides =
-            refreshRateConfigs.getFrameRateOverrides(layers,
-                                                     Fps::fromValue(
+            refreshRateSelector.getFrameRateOverrides(layers,
+                                                      Fps::fromValue(
+                                                              mFdp.ConsumeFloatingPoint<float>()),
+                                                      globalSignals);
+
+    {
+        ftl::FakeGuard guard(kMainThreadContext);
+
+        refreshRateSelector.setPolicy(
+                RefreshRateSelector::
+                        DisplayManagerPolicy{modeId,
+                                             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateSelector.setPolicy(
+                RefreshRateSelector::OverridePolicy{modeId,
+                                                    {Fps::fromValue(
                                                              mFdp.ConsumeFloatingPoint<float>()),
-                                                     globalSignals);
+                                                     Fps::fromValue(
+                                                             mFdp.ConsumeFloatingPoint<float>())}});
+        refreshRateSelector.setPolicy(RefreshRateSelector::NoOverridePolicy{});
 
-    refreshRateConfigs.setDisplayManagerPolicy(
-            {modeId,
-             {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-              Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}});
-    refreshRateConfigs.setActiveModeId(modeId);
+        refreshRateSelector.setActiveMode(modeId,
+                                          Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
+    }
 
-    RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue(
-                                                           mFdp.ConsumeFloatingPoint<float>()),
-                                                   Fps::fromValue(
-                                                           mFdp.ConsumeFloatingPoint<float>()));
-    RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
-                                            Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
+    RefreshRateSelector::isFractionalPairOrMultiple(Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()),
+                                                    Fps::fromValue(
+                                                            mFdp.ConsumeFloatingPoint<float>()));
+    RefreshRateSelector::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
+                                             Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()));
 
     android::mock::TimeStats timeStats;
     RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()),
                                       PowerMode::OFF);
 
-    const auto fpsOpt = displayModes.get(modeId, [](const auto& mode) { return mode->getFps(); });
+    const auto fpsOpt = displayModes.get(modeId).transform(
+            [](const DisplayModePtr& mode) { return mode->getFps(); });
     refreshRateStats.setRefreshRate(*fpsOpt);
 
     refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes));
 }
 
+void SchedulerFuzzer::fuzzPresentLatencyTracker() {
+    scheduler::PresentLatencyTracker tracker;
+    tracker.trackPendingFrame(TimePoint::fromNs(mFdp.ConsumeIntegral<nsecs_t>()),
+                              FenceTime::NO_FENCE);
+}
+
 void SchedulerFuzzer::process() {
     fuzzRefreshRateSelection();
-    fuzzRefreshRateConfigs();
+    fuzzRefreshRateSelector();
+    fuzzPresentLatencyTracker();
     fuzzVSyncModulator();
     fuzzVSyncPredictor();
     fuzzVSyncReactor();
     fuzzLayerHistory();
-    fuzzDispSyncSource();
     fuzzEventThread();
     fuzzVSyncDispatchTimerQueue();
     fuzzOneShotTimer();
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
index 1a49ead..8061a8f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -27,7 +27,6 @@
 #include "Clock.h"
 #include "Layer.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncModulator.h"
@@ -76,23 +75,7 @@
 
     bool isVisible() const override { return true; }
 
-    sp<Layer> createClone() override { return nullptr; }
-};
-
-class FuzzImplVSyncSource : public VSyncSource {
-public:
-    const char* getName() const override { return "fuzz"; }
-
-    void setVSyncEnabled(bool /* enable */) override {}
-
-    void setCallback(Callback* /* callback */) override {}
-
-    void setDuration(std::chrono::nanoseconds /* workDuration */,
-                     std::chrono::nanoseconds /* readyDuration */) override {}
-
-    VSyncData getLatestVSyncData() const override { return {}; }
-
-    void dump(std::string& /* result */) const override {}
+    sp<Layer> createClone(uint32_t /* mirrorRootId */) override { return nullptr; }
 };
 
 class FuzzImplVSyncTracker : public scheduler::VSyncTracker {
@@ -117,6 +100,8 @@
         return true;
     }
 
+    void setRenderRate(Fps) override {}
+
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
             return timePoint;
@@ -144,6 +129,11 @@
         return (scheduler::ScheduleResult)0;
     }
 
+    scheduler::ScheduleResult update(CallbackToken /* token */,
+                                     ScheduleTiming /* scheduleTiming */) override {
+        return (scheduler::ScheduleResult)0;
+    }
+
     scheduler::CancelResult cancel(CallbackToken /* token */) override {
         return (scheduler::CancelResult)0;
     }
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index 973a439..7287dd0 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -44,10 +44,11 @@
 }
 
 java_library_static {
-    name: "layersprotosnano",
+    name: "layersprotoslite",
     host_supported: true,
     proto: {
-        type: "nano",
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
     },
     srcs: ["*.proto"],
     sdk_version: "core_platform",
@@ -56,7 +57,7 @@
             jarjar_rules: "jarjar-rules.txt",
         },
         host: {
-            static_libs: ["libprotobuf-java-nano"],
+            static_libs: ["libprotobuf-java-lite"],
         },
     },
 }
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 52503ba..cdc2706 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -24,6 +24,8 @@
 #include <unordered_map>
 #include <vector>
 
+using android::gui::LayerMetadata;
+
 namespace android {
 namespace surfaceflinger {
 
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 3598308..e9add2e 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -138,6 +138,8 @@
   float requested_corner_radius = 56;
 
   RectProto destination_frame = 57;
+
+  uint32 original_id = 58;
 }
 
 message PositionProto {
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index 13647b6..804a499 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -38,9 +38,13 @@
 
     optional fixed64 magic_number = 1;  /* Must be the first field, set to value in MagicNumber */
     repeated LayersTraceProto entry = 2;
+
+    /* offset between real-time clock and elapsed time clock in nanoseconds.
+       Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */
+    optional fixed64 real_to_elapsed_time_offset_nanos = 3;
 }
 
-/* one window manager trace entry. */
+/* one layers trace entry. */
 message LayersTraceProto {
     /* required: elapsed realtime in nanos since boot of when this entry was logged */
     optional sfixed64 elapsed_realtime_nanos = 1;
@@ -60,4 +64,6 @@
     optional uint32 missed_entries = 6;
 
     repeated DisplayProto displays = 7;
+
+    optional int64 vsync_id = 8;
 }
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index 4f99b19..b0cee9b 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -36,6 +36,11 @@
 
     fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
     repeated TransactionTraceEntry entry = 2;
+
+    /* offset between real-time clock and elapsed time clock in nanoseconds.
+       Calculated as: systemTime(SYSTEM_TIME_REALTIME) - systemTime(SYSTEM_TIME_MONOTONIC) */
+    fixed64 real_to_elapsed_time_offset_nanos = 3;
+    uint32 version = 4;
 }
 
 message TransactionTraceEntry {
@@ -43,18 +48,47 @@
     int64 vsync_id = 2;
     repeated TransactionState transactions = 3;
     repeated LayerCreationArgs added_layers = 4;
-    repeated int32 removed_layers = 5;
+    repeated uint32 destroyed_layers = 5;
     repeated DisplayState added_displays = 6;
     repeated int32 removed_displays = 7;
-    repeated int32 removed_layer_handles = 8;
+    repeated uint32 destroyed_layer_handles = 8;
+    bool displays_changed = 9;
+    repeated DisplayInfo displays = 10;
+}
+
+message DisplayInfo {
+    uint32 layer_stack = 1;
+    int32 display_id = 2;
+    int32 logical_width = 3;
+    int32 logical_height = 4;
+    Transform transform_inverse = 5;
+    Transform transform = 6;
+    bool receives_input = 7;
+    bool is_secure = 8;
+    bool is_primary = 9;
+    bool is_virtual = 10;
+    int32 rotation_flags = 11;
+    int32 transform_hint = 12;
+
 }
 
 message LayerCreationArgs {
-    int32 layer_id = 1;
+    uint32 layer_id = 1;
     string name = 2;
     uint32 flags = 3;
-    int32 parent_id = 4;
-    int32 mirror_from_id = 5;
+    uint32 parent_id = 4;
+    uint32 mirror_from_id = 5;
+    bool add_to_root = 6;
+    uint32 layer_stack_to_mirror = 7;
+}
+
+message Transform {
+    float dsdx = 1;
+    float dtdx = 2;
+    float dtdy = 3;
+    float dsdy = 4;
+    float tx = 5;
+    float ty = 6;
 }
 
 message TransactionState {
@@ -66,11 +100,12 @@
     uint64 transaction_id = 6;
     repeated LayerState layer_changes = 7;
     repeated DisplayState display_changes = 8;
+    repeated uint64 merged_transaction_ids = 9;
 }
 
 // Keep insync with layer_state_t
 message LayerState {
-    int64 layer_id = 1;
+    uint32 layer_id = 1;
     // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
     // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
     // 32 bits and there's no nice way to put 64bit constants into .proto files.
@@ -78,7 +113,7 @@
         eChangesLsbNone = 0;
         ePositionChanged = 0x00000001;
         eLayerChanged = 0x00000002;
-        eSizeChanged = 0x00000004;
+        // unused = 0x00000004;
         eAlphaChanged = 0x00000008;
 
         eMatrixChanged = 0x00000010;
@@ -94,8 +129,7 @@
         eReparent = 0x00008000;
 
         eColorChanged = 0x00010000;
-        eDestroySurface = 0x00020000;
-        eTransformChanged = 0x00040000;
+        eBufferTransformChanged = 0x00040000;
         eTransformToDisplayInverseChanged = 0x00080000;
 
         eCropChanged = 0x00100000;
@@ -161,8 +195,8 @@
     Matrix22 matrix = 11;
     float corner_radius = 12;
     uint32 background_blur_radius = 13;
-    int64 parent_id = 14;
-    int64 relative_parent_id = 15;
+    uint32 parent_id = 14;
+    uint32 relative_parent_id = 15;
 
     float alpha = 16;
     message Color3 {
@@ -217,14 +251,6 @@
     ColorTransformProto color_transform = 25;
     repeated BlurRegion blur_regions = 26;
 
-    message Transform {
-        float dsdx = 1;
-        float dtdx = 2;
-        float dtdy = 3;
-        float dsdy = 4;
-        float tx = 5;
-        float ty = 6;
-    }
     message WindowInfo {
         uint32 layout_params_flags = 1;
         int32 layout_params_type = 2;
@@ -233,7 +259,7 @@
         bool focusable = 5;
         bool has_wallpaper = 6;
         float global_scale_factor = 7;
-        int64 crop_layer_id = 8;
+        uint32 crop_layer_id = 8;
         bool replace_touchable_region_with_crop = 9;
         RectProto touchable_region_crop = 10;
         Transform transform = 11;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 883766b..cf23169 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -67,7 +67,7 @@
     using android::frameworks::displayservice::V1_0::implementation::DisplayService;
     using android::frameworks::displayservice::V1_0::IDisplayService;
 
-    sp<IDisplayService> displayservice = new DisplayService();
+    sp<IDisplayService> displayservice = sp<DisplayService>::make();
     status_t err = displayservice->registerAsService();
 
     // b/141930622
@@ -91,7 +91,7 @@
     // Set uclamp.min setting on all threads, maybe an overkill but we want
     // to cover important threads like RenderEngine.
     if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
-        ALOGW("Couldn't set uclamp.min: %s\n", strerror(errno));
+        ALOGW("Failed to set uclamp.min during boot: %s", strerror(errno));
     }
 
     // The binder threadpool we start will inherit sched policy and priority
@@ -148,14 +148,14 @@
                    IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
 
     // publish gui::ISurfaceComposer, the new AIDL interface
-    sp<SurfaceComposerAIDL> composerAIDL = new SurfaceComposerAIDL(flinger);
+    sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
     sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
                    IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
 
     startDisplayService(); // dependency on SF getting registered above
 
     if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
-        ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
+        ALOGW("Failed to set SCHED_FIFO during boot: %s", strerror(errno));
     }
 
     // run surface flinger in this thread
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 6ecf78b..62b539a 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -23,7 +23,10 @@
 
 cc_test {
     name: "SurfaceFlinger_test",
-    defaults: ["surfaceflinger_defaults"],
+    defaults: [
+        "android.hardware.graphics.common-ndk_shared",
+        "surfaceflinger_defaults",
+    ],
     test_suites: ["device-tests"],
     srcs: [
         "BootDisplayMode_test.cpp",
@@ -34,11 +37,13 @@
         "DisplayConfigs_test.cpp",
         "DisplayEventReceiver_test.cpp",
         "EffectLayer_test.cpp",
+        "LayerBorder_test.cpp",
         "InvalidHandles_test.cpp",
         "LayerCallback_test.cpp",
         "LayerRenderTypeTransaction_test.cpp",
         "LayerState_test.cpp",
         "LayerTransaction_test.cpp",
+        "LayerTrustedPresentationListener_test.cpp",
         "LayerTypeAndRenderTypeTransaction_test.cpp",
         "LayerTypeTransaction_test.cpp",
         "LayerUpdate_test.cpp",
@@ -52,18 +57,16 @@
         "SetFrameRateOverride_test.cpp",
         "SetGeometry_test.cpp",
         "Stress_test.cpp",
-        "SurfaceInterceptor_test.cpp",
+        "TextureFiltering_test.cpp",
         "VirtualDisplay_test.cpp",
         "WindowInfosListener_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
     static_libs: [
-        "libtrace_proto",
         "liblayers_proto",
         "android.hardware.graphics.composer@2.1",
     ],
     shared_libs: [
-        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.common@1.2",
         "libandroid",
         "libbase",
@@ -128,7 +131,6 @@
 }
 
 subdirs = [
-    "fakehwc",
     "hwc2",
     "unittests",
     "utils",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index d70908e..f2874ae 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest.h>
 
+#include <gui/AidlStatusUtil.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
 #include <private/gui/ComposerServiceAIDL.h>
@@ -25,27 +26,34 @@
 
 namespace android {
 
+using gui::aidl_utils::statusTFromBinderStatus;
+
 TEST(BootDisplayModeTest, setBootDisplayMode) {
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    sp<gui::ISurfaceComposer> sf_aidl(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
-    binder::Status status = sf_aidl->getBootDisplayModeSupport(&bootModeSupport);
-    ASSERT_NO_FATAL_FAILURE(status.transactionError());
+    binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
+    ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
     if (bootModeSupport) {
-        ASSERT_EQ(NO_ERROR, sf->setBootDisplayMode(displayToken, 0));
+        status = sf->setBootDisplayMode(displayToken, 0);
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
     }
 }
 
 TEST(BootDisplayModeTest, clearBootDisplayMode) {
     sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-    auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     bool bootModeSupport = false;
     binder::Status status = sf->getBootDisplayModeSupport(&bootModeSupport);
-    ASSERT_NO_FATAL_FAILURE(status.transactionError());
+    ASSERT_NO_FATAL_FAILURE(statusTFromBinderStatus(status));
     if (bootModeSupport) {
         status = sf->clearBootDisplayMode(displayToken);
-        ASSERT_EQ(NO_ERROR, status.transactionError());
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
     }
 }
 
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 47a150d..d74bd55 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -70,12 +70,13 @@
         consumer->setDefaultBufferSize(width, height);
         consumer->setDefaultBufferFormat(format);
 
-        mBufferItemConsumer = new BufferItemConsumer(consumer, GraphicBuffer::USAGE_HW_TEXTURE);
+        mBufferItemConsumer =
+                sp<BufferItemConsumer>::make(consumer, GraphicBuffer::USAGE_HW_TEXTURE);
 
-        mListener = new BufferListener(consumer, callback);
+        mListener = sp<BufferListener>::make(consumer, callback);
         mBufferItemConsumer->setFrameAvailableListener(mListener);
 
-        mSurface = new Surface(producer, true);
+        mSurface = sp<Surface>::make(producer, true);
     }
 
     /* Used by Egl manager. The surface is never displayed. */
@@ -364,7 +365,7 @@
         *outBuffer = mGraphicBuffer;
     }
     if (outFence) {
-        *outFence = new Fence(mFence);
+        *outFence = sp<Fence>::make(mFence);
     } else {
         close(mFence);
     }
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 434c297..69e9a16 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -18,22 +18,26 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#include <android/gui/ISurfaceComposer.h>
 #include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
+#include <gui/AidlStatusUtil.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
 #include <ui/DisplayMode.h>
 #include <ui/DynamicDisplayInfo.h>
 #include <utils/String8.h>
 #include <functional>
 #include "utils/ScreenshotUtils.h"
+#include "utils/WindowInfosListenerUtils.h"
 
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
+using gui::LayerDebugInfo;
+using gui::aidl_utils::statusTFromBinderStatus;
 using ui::ColorMode;
 
 namespace {
@@ -67,12 +71,30 @@
     sp<SurfaceControl> mVirtualSurfaceControl;
 
     void initClient() {
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
     }
 
+    static sp<IBinder> getFirstDisplayToken() {
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) {
+            return nullptr;
+        }
+
+        return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+    }
+
+    static std::optional<uint64_t> getFirstDisplayId() {
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        if (ids.empty()) {
+            return std::nullopt;
+        }
+
+        return ids.front().value;
+    }
+
     void setupBackgroundSurface() {
-        mDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        mDisplay = getFirstDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
         ui::DisplayMode mode;
@@ -149,45 +171,39 @@
     // Anyone else can init the client.
     {
         UIDFaker f(AID_BIN);
-        mComposerClient = new SurfaceComposerClient;
+        mComposerClient = sp<SurfaceComposerClient>::make();
         ASSERT_NO_FATAL_FAILURE(initClient());
     }
 }
 
 TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
-    std::function<bool()> condition = [] {
-        return SurfaceComposerClient::getInternalDisplayToken() != nullptr;
-    };
+    std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
     // Anyone can access display information.
-    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+    ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
 }
 
 TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
     // The following methods are tested with a UID that is not root, graphics,
     // or system, to show that anyone can access them.
     UIDFaker f(AID_BIN);
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ASSERT_TRUE(display != nullptr);
-
-    ui::DisplayMode mode;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
-    Vector<ui::DisplayMode> modes;
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
 }
 
 TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     std::function<status_t()> condition = [=]() {
         ui::DynamicDisplayInfo info;
-        return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+        return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
 }
 
 TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         ui::DisplayPrimaries primaries;
         return SurfaceComposerClient::getDisplayNativePrimaries(display, primaries);
@@ -196,30 +212,19 @@
 }
 
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ui::DisplayModeId defaultMode;
-    bool allowGroupSwitching;
-    float primaryFpsMin;
-    float primaryFpsMax;
-    float appRequestFpsMin;
-    float appRequestFpsMax;
-    status_t res =
-            SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode,
-                                                              &allowGroupSwitching, &primaryFpsMin,
-                                                              &primaryFpsMax, &appRequestFpsMin,
-                                                              &appRequestFpsMax);
+    const auto display = getFirstDisplayToken();
+    gui::DisplayModeSpecs specs;
+    status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &specs);
     ASSERT_EQ(res, NO_ERROR);
+    gui::DisplayModeSpecs setSpecs;
     std::function<status_t()> condition = [=]() {
-        return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode,
-                                                                 allowGroupSwitching, primaryFpsMin,
-                                                                 primaryFpsMax, appRequestFpsMin,
-                                                                 appRequestFpsMax);
+        return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, specs);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
 
 TEST_F(CredentialsTest, SetActiveColorModeTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE);
     };
@@ -271,7 +276,7 @@
 }
 
 TEST_F(CredentialsTest, CaptureTest) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
         DisplayCaptureArgs captureArgs;
@@ -301,39 +306,45 @@
  */
 TEST_F(CredentialsTest, GetLayerDebugInfo) {
     setupBackgroundSurface();
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
 
     // Historically, only root and shell can access the getLayerDebugInfo which
     // is called when we call dumpsys. I don't see a reason why we should change this.
     std::vector<LayerDebugInfo> outLayers;
+    binder::Status status = binder::Status::ok();
     // Check with root.
     {
         UIDFaker f(AID_ROOT);
-        ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
     }
 
     // Check as a shell.
     {
         UIDFaker f(AID_SHELL);
-        ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers));
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
     }
 
     // Check as anyone else.
     {
         UIDFaker f(AID_BIN);
-        ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers));
+        status = sf->getLayerDebugInfo(&outLayers);
+        ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status));
     }
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     bool result = false;
     status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
     ASSERT_EQ(NO_ERROR, error);
     bool hasWideColorMode = false;
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+    SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     const auto& colorModes = info.supportedColorModes;
     for (ColorMode colorMode : colorModes) {
         switch (colorMode) {
@@ -350,7 +361,7 @@
 }
 
 TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto display = getFirstDisplayToken();
     ASSERT_FALSE(display == nullptr);
     std::function<status_t()> condition = [=]() {
         bool result = false;
@@ -360,14 +371,66 @@
 }
 
 TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ASSERT_FALSE(display == nullptr);
+    const auto id = getFirstDisplayId();
+    ASSERT_TRUE(id);
     ui::DynamicDisplayInfo info;
-    SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+    SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
     ColorMode colorMode = info.activeColorMode;
     ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
 }
 
+TEST_F(CredentialsTest, TransactionPermissionTest) {
+    WindowInfosListenerUtils windowInfosListenerUtils;
+    std::string name = "Test Layer";
+    sp<IBinder> token = sp<BBinder>::make();
+    WindowInfo windowInfo;
+    windowInfo.name = name;
+    windowInfo.token = token;
+    sp<SurfaceControl> surfaceControl =
+            mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
+                                           ISurfaceComposerClient::eFXSurfaceBufferState);
+    const Rect crop(0, 0, 100, 100);
+    {
+        UIDFaker f(AID_SYSTEM);
+        Transaction()
+                .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
+                .show(surfaceControl)
+                .setLayer(surfaceControl, INT32_MAX - 1)
+                .setCrop(surfaceControl, crop)
+                .setInputWindowInfo(surfaceControl, windowInfo)
+                .apply();
+    }
+
+    // Called from non privileged process
+    Transaction().setTrustedOverlay(surfaceControl, true);
+    {
+        UIDFaker f(AID_SYSTEM);
+        auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
+            auto foundWindowInfo =
+                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+            if (!foundWindowInfo) {
+                return false;
+            }
+            return !foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
+        };
+        windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndNotTrusted);
+    }
+
+    {
+        UIDFaker f(AID_SYSTEM);
+        Transaction().setTrustedOverlay(surfaceControl, true);
+        auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
+            auto foundWindowInfo =
+                    WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
+            if (!foundWindowInfo) {
+                return false;
+            }
+            return foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
+        };
+        windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndTrusted);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index c58fe48..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -39,105 +39,71 @@
  */
 class RefreshRateRangeTest : public ::testing::Test {
 private:
-    ui::DisplayModeId initialDefaultMode;
-    bool initialAllowGroupSwitching;
-    float initialPrimaryMin;
-    float initialPrimaryMax;
-    float initialAppRequestMin;
-    float initialAppRequestMax;
+    gui::DisplayModeSpecs mSpecs;
 
 protected:
     void SetUp() override {
-        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
-        status_t res =
-                SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
-                                                                  &initialDefaultMode,
-                                                                  &initialAllowGroupSwitching,
-                                                                  &initialPrimaryMin,
-                                                                  &initialPrimaryMax,
-                                                                  &initialAppRequestMin,
-                                                                  &initialAppRequestMax);
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplayId = ids.front().value;
+        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
         ASSERT_EQ(res, NO_ERROR);
     }
 
     void TearDown() override {
-        status_t res =
-                SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode,
-                                                                  initialAllowGroupSwitching,
-                                                                  initialPrimaryMin,
-                                                                  initialPrimaryMax,
-                                                                  initialAppRequestMin,
-                                                                  initialAppRequestMax);
+        status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, mSpecs);
         ASSERT_EQ(res, NO_ERROR);
     }
 
     void testSetAllowGroupSwitching(bool allowGroupSwitching);
 
     sp<IBinder> mDisplayToken;
+    uint64_t mDisplayId;
 };
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
     ui::DynamicDisplayInfo info;
-    status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+    status_t res =
+            SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+                                                               &info);
     const auto& modes = info.supportedDisplayModes;
     ASSERT_EQ(res, NO_ERROR);
     ASSERT_GT(modes.size(), 0);
 
+    gui::DisplayModeSpecs setSpecs;
+    setSpecs.allowGroupSwitching = false;
     for (size_t i = 0; i < modes.size(); i++) {
-        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate);
+        setSpecs.defaultMode = modes[i].id;
+        setSpecs.primaryRanges.physical.min = modes[i].refreshRate;
+        setSpecs.primaryRanges.physical.max = modes[i].refreshRate;
+        setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+        setSpecs.appRequestRanges = setSpecs.primaryRanges;
+        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
         ASSERT_EQ(res, NO_ERROR);
 
-        ui::DisplayModeId defaultConfig;
-        bool allowGroupSwitching;
-        float primaryRefreshRateMin;
-        float primaryRefreshRateMax;
-        float appRequestRefreshRateMin;
-        float appRequestRefreshRateMax;
-        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
-                                                                &allowGroupSwitching,
-                                                                &primaryRefreshRateMin,
-                                                                &primaryRefreshRateMax,
-                                                                &appRequestRefreshRateMin,
-                                                                &appRequestRefreshRateMax);
+        gui::DisplayModeSpecs getSpecs;
+        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
         ASSERT_EQ(res, NO_ERROR);
-        ASSERT_EQ(defaultConfig, i);
-        ASSERT_EQ(allowGroupSwitching, false);
-        ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate);
-        ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate);
+        ASSERT_EQ(setSpecs, getSpecs);
     }
 }
 
 void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
-    status_t res =
-            SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching,
-                                                              0.f, 90.f, 0.f, 90.f);
-    ASSERT_EQ(res, NO_ERROR);
-    ui::DisplayModeId defaultConfig;
-    bool newAllowGroupSwitching;
-    float primaryRefreshRateMin;
-    float primaryRefreshRateMax;
-    float appRequestRefreshRateMin;
-    float appRequestRefreshRateMax;
+    gui::DisplayModeSpecs setSpecs;
+    setSpecs.defaultMode = 0;
+    setSpecs.allowGroupSwitching = allowGroupSwitching;
+    setSpecs.primaryRanges.physical.min = 0;
+    setSpecs.primaryRanges.physical.max = 90;
+    setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+    setSpecs.appRequestRanges = setSpecs.primaryRanges;
 
-    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
-                                                            &newAllowGroupSwitching,
-                                                            &primaryRefreshRateMin,
-                                                            &primaryRefreshRateMax,
-                                                            &appRequestRefreshRateMin,
-                                                            &appRequestRefreshRateMax);
+    status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
     ASSERT_EQ(res, NO_ERROR);
-    ASSERT_EQ(defaultConfig, 0);
-    ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
-    ASSERT_EQ(primaryRefreshRateMin, 0.f);
-    ASSERT_EQ(primaryRefreshRateMax, 90.f);
-    ASSERT_EQ(appRequestRefreshRateMin, 0.f);
-    ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+    gui::DisplayModeSpecs getSpecs;
+    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
+    ASSERT_EQ(res, NO_ERROR);
+    ASSERT_EQ(setSpecs, getSpecs);
 }
 
 TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
diff --git a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
index 0e54664..4c26017 100644
--- a/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
+++ b/services/surfaceflinger/tests/DisplayEventReceiver_test.cpp
@@ -33,10 +33,16 @@
 
     const VsyncEventData& vsyncEventData = parcelableVsyncEventData.vsync;
     EXPECT_NE(std::numeric_limits<size_t>::max(), vsyncEventData.preferredFrameTimelineIndex);
+    EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+            << "Frame timelines length should be greater than 0";
+    EXPECT_LE(static_cast<int64_t>(vsyncEventData.frameTimelinesLength),
+              VsyncEventData::kFrameTimelinesCapacity)
+            << "Frame timelines length should not exceed max capacity";
     EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
             << "Deadline timestamp should be greater than frame time";
-    for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
-        EXPECT_NE(FrameTimelineInfo::INVALID_VSYNC_ID, vsyncEventData.frameTimelines[i].vsyncId);
+    for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
+        EXPECT_NE(gui::FrameTimelineInfo::INVALID_VSYNC_ID,
+                  vsyncEventData.frameTimelines[i].vsyncId);
         EXPECT_GT(vsyncEventData.frameTimelines[i].expectedPresentationTime,
                   vsyncEventData.frameTimelines[i].deadlineTimestamp)
                 << "Expected vsync timestamp should be greater than deadline";
@@ -51,4 +57,4 @@
     }
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 9fa0452..52aa502 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -28,7 +28,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -177,7 +179,9 @@
 }
 
 TEST_F(EffectLayerTest, EffectLayerWithColorNoCrop) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
     const ui::Size& resolution = mode.resolution;
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index ce94dab..40a5d57 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -136,7 +136,7 @@
     }
 
     status_t initClient() override {
-        mClient = new SurfaceComposerClient;
+        mClient = sp<SurfaceComposerClient>::make();
         auto err = mClient->initCheck();
         return err;
     }
@@ -221,10 +221,12 @@
         ProcessState::self()->startThreadPool();
     }
     void SetUp() {
-        mClient = new SurfaceComposerClient;
+        mClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mPrimaryDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ui::DisplayMode mode;
         mClient->getActiveDisplayMode(mPrimaryDisplay, &mode);
         mDisplayWidth = mode.resolution.getWidth();
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index d192a2d..666ce76 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -42,13 +42,13 @@
     sp<SurfaceComposerClient> mScc;
     sp<SurfaceControl> mNotSc;
     void SetUp() override {
-        mScc = new SurfaceComposerClient;
+        mScc = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mScc->initCheck());
         mNotSc = makeNotSurfaceControl();
     }
 
     sp<SurfaceControl> makeNotSurfaceControl() {
-        return new SurfaceControl(mScc, new NotALayer(), nullptr, true);
+        return sp<SurfaceControl>::make(mScc, sp<NotALayer>::make(), 1, "#1");
     }
 };
 
@@ -64,4 +64,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
new file mode 100644
index 0000000..00e134b
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+// TODO: Amend all tests when screenshots become fully reworked for borders
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <chrono> // std::chrono::seconds
+#include <thread> // std::this_thread::sleep_for
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class LayerBorderTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        toHalf3 = ColorTransformHelper::toHalf3;
+        toHalf4 = ColorTransformHelper::toHalf4;
+
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        ASSERT_FALSE(display == nullptr);
+        mColorOrange = toHalf4({255, 140, 0, 255});
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+
+        mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */,
+                                                 0 /* height */, PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceContainer |
+                                                         ISurfaceComposerClient::eNoColorFill,
+                                                 mParentLayer->getHandle());
+        EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer";
+
+        mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
+                                               0 /* height */, PIXEL_FORMAT_RGBA_8888,
+                                               ISurfaceComposerClient::eFXSurfaceEffect |
+                                                       ISurfaceComposerClient::eNoColorFill,
+                                               mContainerLayer->getHandle());
+        EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1";
+
+        mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
+                                               0 /* height */, PIXEL_FORMAT_RGBA_8888,
+                                               ISurfaceComposerClient::eFXSurfaceEffect |
+                                                       ISurfaceComposerClient::eNoColorFill,
+                                               mContainerLayer->getHandle());
+
+        EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2";
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
+            t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+
+            t.setColor(mEffectLayer1, toHalf3(Color::BLUE));
+
+            t.setColor(mEffectLayer2, toHalf3(Color::GREEN));
+        });
+    }
+
+    virtual void TearDown() {
+        // Uncomment the line right below when running any of the tests
+        // std::this_thread::sleep_for (std::chrono::seconds(30));
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+    }
+
+    std::function<half3(Color)> toHalf3;
+    std::function<half4(Color)> toHalf4;
+    sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2;
+    half4 mColorOrange;
+};
+
+TEST_F(LayerBorderTest, OverlappingVisibleRegions) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+        t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
+        t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, EmptyVisibleRegion) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600));
+
+        t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, ZOrderAdjustment) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+        t.setLayer(mParentLayer, 10);
+        t.setLayer(mEffectLayer1, 30);
+        t.setLayer(mEffectLayer2, 20);
+
+        t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, GrandChildHierarchy) {
+    sp<SurfaceControl> containerLayer2 =
+            mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceContainer |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mContainerLayer->getHandle());
+    EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2";
+
+    sp<SurfaceControl> effectLayer3 =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   containerLayer2->getHandle());
+
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+        t.setCrop(effectLayer3, Rect(400, 400, 800, 800));
+        t.setColor(effectLayer3, toHalf3(Color::BLUE));
+
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(effectLayer3);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, TransparentAlpha) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+        t.setAlpha(mEffectLayer1, 0.0f);
+
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, SemiTransparentAlpha) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+        t.setAlpha(mEffectLayer2, 0.5f);
+
+        t.enableBorder(mEffectLayer2, true, 20, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, InvisibleLayers) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+        t.hide(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, LayerWithBuffer) {
+    asTransaction([&](Transaction& t) {
+        t.hide(mEffectLayer1);
+        t.hide(mEffectLayer2);
+        t.show(mContainerLayer);
+
+        sp<SurfaceControl> layer =
+                mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */,
+                                       PIXEL_FORMAT_RGBA_8888,
+                                       ISurfaceComposerClient::eFXSurfaceBufferState,
+                                       mContainerLayer->getHandle());
+
+        sp<GraphicBuffer> buffer =
+                sp<GraphicBuffer>::make(400u, 400u, PIXEL_FORMAT_RGBA_8888, 1u,
+                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                                BufferUsage::COMPOSER_OVERLAY |
+                                                BufferUsage::GPU_TEXTURE,
+                                        "test");
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE);
+
+        t.setBuffer(layer, buffer);
+        t.setPosition(layer, 100, 100);
+        t.show(layer);
+        t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+    });
+}
+
+TEST_F(LayerBorderTest, CustomWidth) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 50, mColorOrange);
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, CustomColor) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+        t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255}));
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) {
+    asTransaction([&](Transaction& t) {
+        t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
+        t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
+
+        t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128}));
+        t.show(mEffectLayer1);
+        t.show(mEffectLayer2);
+        t.show(mContainerLayer);
+    });
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 219db8c..79886bd 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -51,7 +51,7 @@
         LayerTransactionTest::TearDown();
     }
 
-    virtual sp<SurfaceControl> createBufferStateLayer() {
+    virtual sp<SurfaceControl> createLayerWithBuffer() {
         return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
     }
 
@@ -164,7 +164,7 @@
 
 TEST_F(LayerCallbackTest, BufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -183,7 +183,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -206,7 +206,7 @@
 
 TEST_F(LayerCallbackTest, BufferNoColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -228,7 +228,7 @@
 
 TEST_F(LayerCallbackTest, NoBufferColor) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -266,7 +266,7 @@
 
 TEST_F(LayerCallbackTest, OffScreen) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -288,8 +288,8 @@
 
 TEST_F(LayerCallbackTest, MergeBufferNoColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -322,8 +322,8 @@
 
 TEST_F(LayerCallbackTest, MergeNoBufferColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -357,8 +357,8 @@
 
 TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -392,8 +392,8 @@
 }
 TEST_F(LayerCallbackTest, Merge_SameCallback) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback;
@@ -418,7 +418,7 @@
 
 TEST_F(LayerCallbackTest, Merge_SameLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -442,8 +442,8 @@
 }
 
 TEST_F(LayerCallbackTest, Merge_DifferentClients) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
+    sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()),
+            client2(sp<SurfaceComposerClient>::make());
 
     ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
     ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
@@ -485,7 +485,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -510,7 +510,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -541,7 +541,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -579,8 +579,8 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
     sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
 
     Transaction transaction1, transaction2;
     CallbackHelper callback1, callback2;
@@ -620,8 +620,8 @@
 }
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
+    sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()),
+            client2(sp<SurfaceComposerClient>::make());
     ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
     ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
 
@@ -669,8 +669,8 @@
 }
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
+    sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()),
+            client2(sp<SurfaceComposerClient>::make());
     ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
     ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
 
@@ -730,8 +730,8 @@
 }
 
 TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
+    sp<SurfaceComposerClient> client1(sp<SurfaceComposerClient>::make()),
+            client2(sp<SurfaceComposerClient>::make());
 
     ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
     ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
@@ -799,7 +799,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -823,7 +823,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -858,7 +858,7 @@
 
 TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     // Normal call to set up test
     Transaction transaction;
@@ -901,7 +901,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -925,7 +925,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -971,7 +971,7 @@
 // TODO (b/183181768): Fix & re-enable
 TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback1;
@@ -1015,7 +1015,7 @@
 
 TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1039,7 +1039,7 @@
 
 TEST_F(LayerCallbackTest, ExpectedPresentTime) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1050,7 +1050,10 @@
     }
 
     const Vsync vsync = waitForNextVsync();
-    transaction.setFrameTimelineInfo({vsync.vsyncId, 0});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = vsync.vsyncId;
+    ftInfo.inputEventId = 0;
+    transaction.setFrameTimelineInfo(ftInfo);
     transaction.apply();
 
     ExpectedResult expected;
@@ -1062,8 +1065,8 @@
 // b202394221
 TEST_F(LayerCallbackTest, EmptyBufferStateChanges) {
     sp<SurfaceControl> bufferLayer, emptyBufferLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayerWithBuffer());
+    ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1117,7 +1120,7 @@
 
 TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
     sp<SurfaceControl> offscreenLayer =
             createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
                           ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
@@ -1148,7 +1151,7 @@
 
 TEST_F(LayerCallbackTest, TransactionCommittedCallback_BSL) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
 
     Transaction transaction;
     CallbackHelper callback;
@@ -1221,4 +1224,75 @@
     EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
 }
 
+TEST_F(LayerCallbackTest, SetNullBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+                              /*setBackgroundColor=*/false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+
+    transaction.setBuffer(layer, nullptr);
+    transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED_NULL,
+                            ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+
+    err = fillTransaction(transaction, &callback, layer, /*setBuffer=*/true,
+                          /*setBackgroundColor=*/false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+}
+
+TEST_F(LayerCallbackTest, SetNullBufferOnLayerWithoutBuffer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayerWithBuffer());
+
+    Transaction transaction;
+    transaction.setBuffer(layer, nullptr);
+    CallbackHelper callback;
+    transaction.addTransactionCompletedCallback(callback.function, callback.getContext());
+    transaction.apply();
+
+    {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED,
+                            ExpectedResult::PreviousBuffer::NOT_RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+    }
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 0e2bc3d..b8068f7 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -328,7 +328,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
 
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
@@ -352,7 +352,7 @@
         shot->expectColor(bottom, Color::BLACK);
     }
 
-    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+    buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
 
     ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
     ASSERT_NO_FATAL_FAILURE(
@@ -399,7 +399,7 @@
             .apply();
     ASSERT_NO_FATAL_FAILURE(
             fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layerR, Color::RED, 32, 32));
     getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
 }
 
@@ -482,7 +482,7 @@
     }
 }
 
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// RED: Color layer base color and Layer buffer fill
 // BLUE: prior background color
 // GREEN: final background color
 // BLACK: no color or fill
@@ -516,7 +516,7 @@
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
             if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+                ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, fillColor, width, height));
                 expectedColor = fillColor;
             }
             Transaction().setCrop(layer, Rect(0, 0, width, height)).apply();
@@ -544,6 +544,7 @@
             .apply();
 
     {
+        SCOPED_TRACE("final color");
         auto shot = screenshot();
         shot->expectColor(Rect(0, 0, width, height), finalColor);
         shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
@@ -832,7 +833,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect crop(8, 8, 24, 24);
 
     Transaction().setCrop(layer, crop).apply();
@@ -863,7 +864,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("empty rect");
@@ -894,7 +895,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32, 64, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
 
@@ -944,7 +945,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     const Rect crop(8, 8, 24, 24);
     Transaction().setPosition(layer, 32, 32).setCrop(layer, crop).apply();
@@ -972,7 +973,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     const Rect frame(8, 8, 24, 24);
 
     Transaction t;
@@ -988,7 +989,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction t;
     {
@@ -1014,7 +1015,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 10, 10));
 
     // A layer with a buffer will have a computed size that matches the buffer size.
     auto shot = getScreenCapture();
@@ -1026,11 +1027,11 @@
     sp<SurfaceControl> parent, child;
     ASSERT_NO_FATAL_FAILURE(
             parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1047,7 +1048,7 @@
 
     ASSERT_NO_FATAL_FAILURE(
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
 
     Transaction().reparent(child, parent).apply();
 
@@ -1061,7 +1062,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     std::this_thread::sleep_for(500ms);
 
@@ -1080,9 +1081,9 @@
             child = createLayer("test", 10, 10, ISurfaceComposerClient::eFXSurfaceBufferState));
     Transaction().reparent(child, parent).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(parent, Color::RED, 32, 32));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::BLUE, 10, 10));
     Rect childDst(0, 16, 32, 32);
     Transaction t;
     TransactionUtils::setFrame(t, child, Rect(0, 0, 10, 10), childDst);
@@ -1099,7 +1100,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     auto shot = getScreenCapture();
     shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -1111,7 +1112,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 1");
@@ -1120,7 +1121,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 2");
@@ -1129,7 +1130,7 @@
         shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     {
         SCOPED_TRACE("set buffer 3");
@@ -1148,7 +1149,7 @@
     ASSERT_NO_FATAL_FAILURE(
             layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::RED, 64, 64));
 
     {
         SCOPED_TRACE("set layer 1 buffer red");
@@ -1156,7 +1157,7 @@
         shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::BLUE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer blue");
@@ -1166,7 +1167,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer1, Color::GREEN, 64, 64));
     {
         SCOPED_TRACE("set layer 1 buffer green");
         auto shot = getScreenCapture();
@@ -1175,7 +1176,7 @@
         shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
     }
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer2, Color::WHITE, 32, 32));
 
     {
         SCOPED_TRACE("set layer 2 buffer white");
@@ -1197,7 +1198,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+        buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1230,7 +1231,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+        buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1263,7 +1264,7 @@
 
     size_t idx = 0;
     for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+        buffer = sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
         Color color = colors[idx % colors.size()];
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
         idx++;
@@ -1344,7 +1345,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     sp<Fence> fence;
@@ -1370,7 +1371,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     sp<Fence> fence = Fence::NO_FENCE;
@@ -1388,7 +1389,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
@@ -1404,7 +1405,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     HdrMetadata hdrMetadata;
@@ -1422,7 +1423,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Region region;
@@ -1440,7 +1441,7 @@
             layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
     TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
 
     Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
@@ -1635,6 +1636,65 @@
         getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
     }
 }
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBuffer) {
+    const Rect bounds(0, 0, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags, "test");
+
+    ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bounds, Color::GREEN));
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("before null buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::GREEN);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer removes buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("after null buffer");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetNullBufferOnLayerWithoutBuffer) {
+    const Rect bounds(0, 0, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    {
+        SCOPED_TRACE("starting state");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer has no effect");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+
+    Transaction().setBuffer(layer, nullptr).apply();
+    {
+        SCOPED_TRACE("null buffer has no effect");
+        auto shot = getScreenCapture();
+        shot->expectColor(bounds, Color::BLACK);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 094b0ff..2181370 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -35,7 +35,7 @@
     args.frameScaleX = 2;
     args.frameScaleY = 4;
     args.captureSecureLayers = true;
-    args.displayToken = new BBinder();
+    args.displayToken = sp<BBinder>::make();
     args.width = 10;
     args.height = 20;
     args.useIdentityTransform = true;
@@ -67,8 +67,8 @@
     args.frameScaleX = 2;
     args.frameScaleY = 4;
     args.captureSecureLayers = true;
-    args.layerHandle = new BBinder();
-    args.excludeHandles = {new BBinder(), new BBinder()};
+    args.layerHandle = sp<BBinder>::make();
+    args.excludeHandles = {sp<BBinder>::make(), sp<BBinder>::make()};
     args.childrenOnly = false;
     args.grayscale = true;
 
@@ -90,13 +90,12 @@
     ASSERT_EQ(args.grayscale, args2.grayscale);
 }
 
-TEST(LayerStateTest, ParcellingScreenCaptureResults) {
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) {
     ScreenCaptureResults results;
-    results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0);
-    results.fence = new Fence(dup(fileno(tmpfile())));
+    results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+    results.fenceResult = sp<Fence>::make(dup(fileno(tmpfile())));
     results.capturedSecureLayers = true;
     results.capturedDataspace = ui::Dataspace::DISPLAY_P3;
-    results.result = BAD_VALUE;
 
     Parcel p;
     results.writeToParcel(&p);
@@ -110,10 +109,41 @@
     ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth());
     ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight());
     ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat());
-    ASSERT_EQ(results.fence->isValid(), results2.fence->isValid());
+    ASSERT_TRUE(results.fenceResult.ok());
+    ASSERT_TRUE(results2.fenceResult.ok());
+    ASSERT_EQ(results.fenceResult.value()->isValid(), results2.fenceResult.value()->isValid());
     ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers);
     ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace);
-    ASSERT_EQ(results.result, results2.result);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithNoFenceOrError) {
+    ScreenCaptureResults results;
+
+    Parcel p;
+    results.writeToParcel(&p);
+    p.setDataPosition(0);
+
+    ScreenCaptureResults results2;
+    results2.readFromParcel(&p);
+
+    ASSERT_TRUE(results2.fenceResult.ok());
+    ASSERT_EQ(results2.fenceResult.value(), Fence::NO_FENCE);
+}
+
+TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFenceError) {
+    ScreenCaptureResults results;
+    results.fenceResult = base::unexpected(BAD_VALUE);
+
+    Parcel p;
+    results.writeToParcel(&p);
+    p.setDataPosition(0);
+
+    ScreenCaptureResults results2;
+    results2.readFromParcel(&p);
+
+    ASSERT_FALSE(results.fenceResult.ok());
+    ASSERT_FALSE(results2.fenceResult.ok());
+    ASSERT_EQ(results.fenceResult.error(), results2.fenceResult.error());
 }
 
 } // namespace test
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 6bd7920..badd5be 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -23,9 +23,11 @@
 
 #include <cutils/properties.h>
 #include <gtest/gtest.h>
+#include <gui/AidlStatusUtil.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
 #include <ui/DisplayMode.h>
 
 #include "BufferGenerator.h"
@@ -39,13 +41,14 @@
 class LayerTransactionTest : public ::testing::Test {
 protected:
     void SetUp() override {
-        mClient = new SurfaceComposerClient;
+        mClient = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
 
         ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
 
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+        sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+        binder::Status status = sf->getColorManagement(&mColorManagementUsed);
+        ASSERT_NO_FATAL_FAILURE(gui::aidl_utils::statusTFromBinderStatus(status));
 
         mCaptureArgs.displayToken = mDisplay;
     }
@@ -134,13 +137,16 @@
         postBufferQueueLayerBuffer(layer);
     }
 
-    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
+    virtual void fillBufferLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                      int32_t bufferWidth, int32_t bufferHeight) {
         sp<GraphicBuffer> buffer =
-                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
-                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
-                                  "test");
+                sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+                                        static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
+                                        1u,
+                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                                BufferUsage::COMPOSER_OVERLAY |
+                                                BufferUsage::GPU_TEXTURE,
+                                        "test");
         TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
                                                  color);
         Transaction().setBuffer(layer, buffer).apply();
@@ -153,7 +159,7 @@
                 fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             case ISurfaceComposerClient::eFXSurfaceBufferState:
-                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+                fillBufferLayerColor(layer, color, bufferWidth, bufferHeight);
                 break;
             default:
                 ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
@@ -206,10 +212,13 @@
                                               const Color& topRight, const Color& bottomLeft,
                                               const Color& bottomRight) {
         sp<GraphicBuffer> buffer =
-                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
-                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE,
-                                  "test");
+                sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+                                        static_cast<uint32_t>(bufferHeight), PIXEL_FORMAT_RGBA_8888,
+                                        1u,
+                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                                BufferUsage::COMPOSER_OVERLAY |
+                                                BufferUsage::GPU_TEXTURE,
+                                        "test");
 
         ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
 
@@ -224,7 +233,7 @@
                                                  Rect(halfW, halfH, bufferWidth, bufferHeight),
                                                  bottomRight);
 
-        Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+        Transaction().setBuffer(layer, buffer).apply();
     }
 
     std::unique_ptr<ScreenCapture> screenshot() {
@@ -280,7 +289,9 @@
 
 private:
     void SetUpDisplay() {
-        mDisplay = mClient->getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
index ef992d6..cbd54e7 100644
--- a/services/surfaceflinger/tests/LayerTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -32,7 +32,7 @@
 
     Transaction().setTransformToDisplayInverse(layer, false).apply();
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::GREEN, 32, 32));
 
     Transaction().setTransformToDisplayInverse(layer, true).apply();
 }
@@ -80,7 +80,7 @@
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
     const auto producer = layer->getIGraphicBufferProducer();
-    const sp<IProducerListener> stubListener(new StubProducerListener);
+    const sp<IProducerListener> stubListener(sp<StubProducerListener>::make());
     IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
     ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
 
@@ -154,6 +154,36 @@
 
     ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
 }
+
+// b/245052266 - we possible could support blur and a buffer at the same layer but
+// might break existing assumptions at higher level. This test captures the current
+// expectations. A layer drawing a buffer will not support blur.
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverBlur) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setBackgroundBlurRadius(layer, 5).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverBlur");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
+TEST_F(LayerTransactionTest, BufferTakesPriorityOverColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setColor(layer, {Color::GREEN.r, Color::GREEN.g, Color::GREEN.b}).apply();
+    {
+        SCOPED_TRACE("BufferTakesPriorityOverColor");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+    }
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
new file mode 100644
index 0000000..2be8d3b
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+struct PresentationCallbackHelper {
+    void callbackArrived(bool state) {
+        std::unique_lock l(mMutex);
+        mGotCallback = true;
+        mState = state;
+        mCondition.notify_all();
+    }
+    bool awaitCallback() {
+        std::unique_lock l(mMutex);
+        mGotCallback = false;
+        mCondition.wait_for(l, 5000ms);
+        EXPECT_TRUE(mGotCallback);
+        return mState;
+    }
+
+    bool mState;
+    bool mGotCallback;
+    std::mutex mMutex;
+    std::condition_variable mCondition;
+};
+
+TrustedPresentationThresholds thresh() {
+    TrustedPresentationThresholds thresholds;
+    thresholds.minAlpha = 1.0;
+    thresholds.minFractionRendered = 1.0;
+    thresholds.stabilityRequirementMs = 100;
+    return thresholds;
+}
+
+class LayerTrustedPresentationListenerTest : public LayerTransactionTest {
+public:
+    void SetUp() override {
+        LayerTransactionTest::SetUp();
+        mainLayer = makeLayer();
+        thresholds = thresh();
+    }
+
+    void TearDown() override {
+        LayerTransactionTest::TearDown();
+        mCallback = nullptr;
+        t.reparent(mainLayer, nullptr).apply();
+        mainLayer = nullptr;
+    }
+
+    void thresholdsPrepared() {
+        t.show(mainLayer)
+                .setLayer(mainLayer, INT32_MAX)
+                .setTrustedPresentationCallback(
+                        mainLayer,
+                        [&](void* context, bool state) {
+                            PresentationCallbackHelper* helper =
+                                    (PresentationCallbackHelper*)context;
+                            helper->callbackArrived(state);
+                        },
+                        thresholds, &pch, mCallback)
+                .setPosition(mainLayer, 100, 100)
+                .apply();
+    }
+
+    sp<SurfaceControl> makeLayer() {
+        sp<SurfaceControl> layer =
+                createLayer("test", 100, 100, ISurfaceComposerClient::eFXSurfaceBufferState,
+                            mBlackBgSurface.get());
+        fillBufferLayerColor(layer, Color::RED, 100, 100);
+        return layer;
+    }
+    sp<SurfaceControl> mainLayer;
+    PresentationCallbackHelper pch;
+    SurfaceComposerClient::Transaction t;
+    TrustedPresentationThresholds thresholds;
+    sp<SurfaceComposerClient::PresentationCallbackRAII> mCallback;
+};
+
+// The layer is fully presented with the default test setup.
+TEST_F(LayerTrustedPresentationListenerTest, callback_arrives) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// A hidden layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, hiding_layer_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.hide(mainLayer).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// A fully obscured layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// Even if the layer obscuring us has an Alpha channel, we are still considered
+// obscured.
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_transparency_clears_state) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setFlags(otherLayer, 0, layer_state_t::eLayerOpaque)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+// We can't be presented if our alpha is below the threshold.
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_threshold) {
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.9).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 1.0).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// Verify that the passed in threshold is actually respected!
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_other_threshold) {
+    thresholds.minAlpha = 0.8;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.8).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(mainLayer, 0.9).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+// (86*86)/(100*100) = 0.73...so a crop of 86x86 is below the threshold
+// (87*87)/(100*100) = 0.76...so a crop of 87x87 is above the threshold!
+TEST_F(LayerTrustedPresentationListenerTest, crop_below_threshold) {
+    thresholds.minFractionRendered = 0.75;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setCrop(mainLayer, Rect(0, 0, 86, 86)).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setCrop(mainLayer, Rect(0, 0, 87, 87)).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, scale_below_threshold) {
+    thresholds.minFractionRendered = 0.64;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+    // 0.8 = sqrt(0.64)
+    t.setMatrix(mainLayer, 0.79, 0, 0, 0.79).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(mainLayer, 0.81, 0, 0, 0.81).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_1) {
+    thresholds.minFractionRendered = 0.75;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.49, 0, 0, 0.49).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.51, 0, 0, 0.51).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_2) {
+    thresholds.minFractionRendered = 0.9;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.3, 0, 0, 0.3).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+    t.setMatrix(otherLayer, 0.33, 0, 0, 0.33).apply();
+    EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_alpha) {
+    thresholds.minFractionRendered = 0.9;
+    thresholdsPrepared();
+    EXPECT_TRUE(pch.awaitCallback());
+
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .setAlpha(otherLayer, 0.01)
+            .apply();
+    EXPECT_FALSE(pch.awaitCallback());
+    t.setAlpha(otherLayer, 0.0).apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_display_overlay) {
+    auto otherLayer = makeLayer();
+    t.show(otherLayer)
+            .setPosition(otherLayer, 100, 100)
+            .setLayer(otherLayer, INT32_MAX)
+            .setFlags(otherLayer, layer_state_t::eLayerSkipScreenshot,
+                      layer_state_t::eLayerSkipScreenshot)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .show(mainLayer)
+            .setPosition(mainLayer, 100, 100)
+            .setTrustedPresentationCallback(
+                    mainLayer,
+                    [&](void* context, bool state) {
+                        PresentationCallbackHelper* helper = (PresentationCallbackHelper*)context;
+                        helper->callbackArrived(state);
+                    },
+                    thresholds, &pch, mCallback)
+            .apply();
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_non_overlapping_bounds) {
+    thresholds.minFractionRendered = 0.5;
+    auto otherLayer1 = makeLayer();
+    auto otherLayer2 = makeLayer();
+    t.show(otherLayer1)
+            .show(otherLayer2)
+            .setPosition(otherLayer1, 100, 25)
+            .setLayer(otherLayer1, INT32_MAX)
+            .setPosition(otherLayer2, 100, 175)
+            .setLayer(otherLayer2, INT32_MAX)
+            .setLayer(mainLayer, INT32_MAX - 1)
+            .show(mainLayer)
+            .setPosition(mainLayer, 100, 100)
+            .setTrustedPresentationCallback(
+                    mainLayer,
+                    [&](void* context, bool state) {
+                        PresentationCallbackHelper* helper = (PresentationCallbackHelper*)context;
+                        helper->callbackArrived(state);
+                    },
+                    thresholds, &pch, mCallback)
+            .apply();
+
+    EXPECT_TRUE(pch.awaitCallback());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 9cb617a..f247c9f 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -799,7 +799,7 @@
     sp<Surface> surface = layer->getSurface();
 
     sp<GraphicBuffer> buffer =
-            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test");
+            sp<GraphicBuffer>::make(width, height, PIXEL_FORMAT_RGBX_8888, 1, kUsageFlags, "test");
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
 
@@ -815,7 +815,7 @@
         shot->expectColor(crop, Color::BLACK);
     }
 
-    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
+    buffer = sp<GraphicBuffer>::make(width, height, PIXEL_FORMAT_RGBA_8888, 1, kUsageFlags, "test");
     ASSERT_NO_FATAL_FAILURE(
             TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
 
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index e1a7ecc..867eddb 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index a921aa8..0ea0824 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -18,6 +18,7 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+#include <android-base/properties.h>
 #include <private/android_filesystem_config.h>
 #include "LayerTransactionTest.h"
 #include "utils/TransactionUtils.h"
@@ -29,8 +30,10 @@
     virtual void SetUp() {
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         mParentLayer = createColorLayer("Parent layer", Color::RED);
@@ -117,15 +120,20 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
     }
 
-    // Remove child layer
+    if (base::GetBoolProperty("debug.sf.enable_legacy_frontend", true)) {
+        GTEST_SKIP() << "Skipping test because mirroring behavior changes with legacy frontend";
+    }
+
+    // Remove child layer and verify we can still mirror the layer when
+    // its offscreen.
     Transaction().reparent(mChildLayer, nullptr).apply();
     {
         SCOPED_TRACE("Removed Child Layer");
         auto shot = screenshot();
         // Grandchild mirror
-        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
         // Child mirror
-        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
     }
 
     // Add grandchild layer to offscreen layer
@@ -134,9 +142,9 @@
         SCOPED_TRACE("Added Grandchild Layer");
         auto shot = screenshot();
         // Grandchild mirror
-        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
         // Child mirror
-        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
     }
 
     // Add child layer
@@ -193,14 +201,14 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    sp<SurfaceControl> bufferStateLayer =
-            createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+    sp<SurfaceControl> layer =
+            createLayer("Layer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
-    Transaction().show(bufferStateLayer).apply();
+    fillBufferLayerColor(layer, Color::BLUE, 200, 200);
+    Transaction().show(layer).apply();
 
     {
-        SCOPED_TRACE("Initial Mirror BufferStateLayer");
+        SCOPED_TRACE("Initial Mirror Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
@@ -208,9 +216,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+    fillBufferLayerColor(layer, Color::WHITE, 200, 200);
     {
-        SCOPED_TRACE("Update BufferStateLayer");
+        SCOPED_TRACE("Update Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
@@ -218,9 +226,9 @@
         shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
     }
 
-    Transaction().reparent(bufferStateLayer, nullptr).apply();
+    Transaction().reparent(layer, nullptr).apply();
     {
-        SCOPED_TRACE("Removed BufferStateLayer");
+        SCOPED_TRACE("Removed Layer");
         auto shot = screenshot();
         // Buffer mirror
         shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
@@ -231,7 +239,10 @@
 
 // Test that the mirror layer is initially offscreen.
 TEST_F(MirrorLayerTest, InitialMirrorState) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
@@ -275,7 +286,9 @@
 
 // Test that a mirror layer can be screenshot when offscreen
 TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    ASSERT_FALSE(ids.empty());
+    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
     ui::DisplayMode mode;
     SurfaceComposerClient::getActiveDisplayMode(display, &mode);
     const ui::Size& size = mode.resolution;
@@ -283,7 +296,7 @@
     sp<SurfaceControl> grandchild =
             createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
                         mChildLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(grandchild, Color::BLUE, 50, 50));
     Rect childBounds = Rect(50, 50, 450, 450);
 
     asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 1ed6c65..15ff696 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -35,7 +35,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
         SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
 
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index 50a4092..9cebf11 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -33,7 +33,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         // Back layer
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index a6d7f58..c23fb9b 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -85,7 +85,8 @@
                              sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
                              ReleaseBufferCallbackHelper& releaseCallback) {
         Transaction t;
-        t.setBuffer(layer, buffer, fence, id.framenumber, releaseCallback.getCallback());
+        t.setBuffer(layer, buffer, fence, id.framenumber, 0 /* producerId */,
+                    releaseCallback.getCallback());
         t.addTransactionCompletedCallback(callback.function, callback.getContext());
         t.apply();
     }
@@ -110,10 +111,10 @@
     }
 
     static sp<GraphicBuffer> getBuffer() {
-        return new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                 BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                         BufferUsage::COMPOSER_OVERLAY,
-                                 "test");
+        return sp<GraphicBuffer>::make(32u, 32u, PIXEL_FORMAT_RGBA_8888, 1u,
+                                       BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                               BufferUsage::COMPOSER_OVERLAY,
+                                       "test");
     }
     static uint64_t generateFrameNumber() {
         static uint64_t sFrameNumber = 0;
@@ -301,7 +302,7 @@
 
     Transaction t;
     t.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                releaseCallback->getCallback());
+                0 /* producerId */, releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
     t.setDesiredPresentTime(time);
@@ -317,7 +318,7 @@
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
     t.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                releaseCallback->getCallback());
+                0 /* producerId */, releaseCallback->getCallback());
     t.addTransactionCompletedCallback(transactionCallback.function,
                                       transactionCallback.getContext());
     t.setDesiredPresentTime(time);
@@ -332,8 +333,10 @@
 }
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) {
-    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
-    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+    sp<TransactionCompletedListener> firstCompletedListener =
+            sp<TransactionCompletedListener>::make();
+    sp<TransactionCompletedListener> secondCompletedListener =
+            sp<TransactionCompletedListener>::make();
 
     CallbackHelper callback1, callback2;
 
@@ -360,7 +363,7 @@
 
     Transaction transaction1;
     transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           releaseCallback->getCallback());
+                           0 /* producerId */, releaseCallback->getCallback());
     transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());
 
     // Set a different TransactionCompletedListener to mimic a second process
@@ -395,14 +398,14 @@
     // Create transaction with a buffer.
     Transaction transaction;
     transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                          releaseCallback->getCallback());
+                          0 /* producerId */, releaseCallback->getCallback());
 
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
 
     // Call setBuffer on the same transaction with a different buffer.
     transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                          releaseCallback->getCallback());
+                          0 /* producerId */, releaseCallback->getCallback());
 
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
@@ -417,7 +420,7 @@
     // Create transaction with a buffer.
     Transaction transaction1;
     transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                           releaseCallback->getCallback());
+                           0 /* producerId */, releaseCallback->getCallback());
 
     sp<GraphicBuffer> secondBuffer = getBuffer();
     ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
@@ -425,7 +428,7 @@
     // Create a second transaction with a new buffer for the same layer.
     Transaction transaction2;
     transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           releaseCallback->getCallback());
+                           0 /* producerId */, releaseCallback->getCallback());
 
     // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
     transaction1.merge(std::move(transaction2));
@@ -433,8 +436,10 @@
 }
 
 TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
-    sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
-    sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+    sp<TransactionCompletedListener> firstCompletedListener =
+            sp<TransactionCompletedListener>::make();
+    sp<TransactionCompletedListener> secondCompletedListener =
+            sp<TransactionCompletedListener>::make();
 
     TransactionCompletedListener::setInstance(firstCompletedListener);
 
@@ -446,7 +451,7 @@
 
     Transaction transaction1;
     transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                           releaseCallback->getCallback());
+                           0 /* producerId */, releaseCallback->getCallback());
 
     // Sent a second buffer to allow the first buffer to get released.
     sp<GraphicBuffer> secondBuffer = getBuffer();
@@ -454,7 +459,7 @@
 
     Transaction transaction2;
     transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
-                           releaseCallback->getCallback());
+                           0 /* producerId */, releaseCallback->getCallback());
 
     // Set a different TransactionCompletedListener to mimic a second process
     TransactionCompletedListener::setInstance(secondCompletedListener);
@@ -475,10 +480,11 @@
     // Create transaction with a buffer.
     Transaction transaction;
     transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
-                          releaseCallback->getCallback());
+                          0 /* producerId */, releaseCallback->getCallback());
 
     // Call setBuffer on the same transaction with a null buffer.
-    transaction.setBuffer(layer, nullptr, std::nullopt, 0, releaseCallback->getCallback());
+    transaction.setBuffer(layer, nullptr, std::nullopt, 0, 0 /* producerId */,
+                          releaseCallback->getCallback());
 
     ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
 }
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 6a7d8b8..013694f 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -30,7 +30,9 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        ASSERT_FALSE(ids.empty());
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
         ASSERT_FALSE(display == nullptr);
 
         ui::DisplayMode mode;
@@ -220,6 +222,14 @@
     mCapture->checkPixel(0, 0, 200, 200, 200);
 }
 
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
+    mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
+    ScreenCapture::captureDisplay(&mCapture, mCaptureArgs);
+    mCapture->expectBGColor(0, 0);
+    // Doesn't capture FG layer which is at 64, 64
+    mCapture->expectBGColor(64, 64);
+}
+
 // Like the last test but verifies that children are also exclude.
 TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
     auto fgHandle = mFGSurfaceControl->getHandle();
@@ -371,7 +381,7 @@
     ScreenCaptureResults captureResults;
     ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults));
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(child, Color::RED, 32, 32));
     SurfaceComposerClient::Transaction().apply(true);
     ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults));
     ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
@@ -449,8 +459,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -484,8 +494,8 @@
                                                  ISurfaceComposerClient::eFXSurfaceBufferState,
                                                  redLayer.get());
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(blueLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(blueLayer, Color::BLUE, 30, 30));
 
     SurfaceComposerClient::Transaction()
             .setLayer(redLayer, INT32_MAX - 1)
@@ -519,7 +529,7 @@
 
 TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
     LayerCaptureArgs args;
-    args.layerHandle = new BBinder();
+    args.layerHandle = sp<BBinder>::make();
 
     ScreenCaptureResults captureResults;
     // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
@@ -549,8 +559,8 @@
                         ISurfaceComposerClient::eSecure |
                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                         redLayer.get());
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(secureLayer, Color::BLUE, 30, 30));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(secureLayer, Color::BLUE, 30, 30));
 
     auto redLayerHandle = redLayer->getHandle();
     Transaction()
@@ -803,7 +813,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
 
     LayerCaptureArgs captureArgs;
@@ -825,7 +835,7 @@
     mCapture->expectColor(Rect(0, 0, 32, 32),
                           Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
 
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLUE, 32, 32));
     ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectedColor = luminance.b * 255;
@@ -838,7 +848,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
 
@@ -865,7 +875,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
@@ -885,7 +895,7 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
                                                 mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLACK, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferLayerColor(layer, Color::BLACK, 32, 32));
     Transaction()
             .show(layer)
             .setLayer(layer, INT32_MAX)
diff --git a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
index 4efec77..e43ef95 100644
--- a/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
+++ b/services/surfaceflinger/tests/SetFrameRateOverride_test.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
+#include <android/gui/ISurfaceComposer.h>
 #include <gtest/gtest.h>
 #include <gui/DisplayEventReceiver.h>
-#include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <sys/epoll.h>
 #include <algorithm>
@@ -24,12 +24,14 @@
 namespace android {
 namespace {
 using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
+using gui::ISurfaceComposer;
 
 class SetFrameRateOverrideTest : public ::testing::Test {
 protected:
     void SetUp() override {
-        const ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp;
-        const ISurfaceComposer::EventRegistrationFlags eventRegistration = {
+        const ISurfaceComposer::VsyncSource vsyncSource =
+                ISurfaceComposer::VsyncSource::eVsyncSourceApp;
+        const EventRegistrationFlags eventRegistration = {
                 ISurfaceComposer::EventRegistration::frameRateOverride};
 
         mDisplayEventReceiver =
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index e9b6ba0..03201f7 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -32,7 +32,7 @@
 
 TEST(SurfaceFlingerStress, create_and_destroy) {
     auto do_stress = []() {
-        sp<SurfaceComposerClient> client = new SurfaceComposerClient;
+        sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
         ASSERT_EQ(NO_ERROR, client->initCheck());
         for (int j = 0; j < 1000; j++) {
             auto surf = client->createSurface(String8("t"), 100, 100,
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
deleted file mode 100644
index 68cd45e..0000000
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ /dev/null
@@ -1,967 +0,0 @@
-/*
- * Copyright (C) 2016 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"
-#pragma clang diagnostic ignored "-Wextra"
-
-#include <android-base/stringprintf.h>
-#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-#include <ui/DisplayMode.h>
-
-#include <fstream>
-#include <random>
-#include <thread>
-
-namespace android {
-
-using Transaction = SurfaceComposerClient::Transaction;
-using SurfaceChange = surfaceflinger::SurfaceChange;
-using Trace = surfaceflinger::Trace;
-using Increment = surfaceflinger::Increment;
-
-constexpr uint32_t BUFFER_UPDATES = 18;
-constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
-constexpr uint32_t SIZE_UPDATE = 134;
-constexpr uint32_t STACK_UPDATE = 1;
-constexpr int32_t RELATIVE_Z = 42;
-constexpr float ALPHA_UPDATE = 0.29f;
-constexpr float CORNER_RADIUS_UPDATE = 0.2f;
-constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
-constexpr float POSITION_UPDATE = 121;
-const Rect CROP_UPDATE(16, 16, 32, 32);
-const float SHADOW_RADIUS_UPDATE = 35.0f;
-std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
-
-const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
-constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
-constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
-constexpr auto LAYER_NAME = "Layer Create and Delete Test";
-
-constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y*outBuffer.stride + x));
-            pixel[0] = r;
-            pixel[1] = g;
-            pixel[2] = b;
-            pixel[3] = 255;
-        }
-    }
-    ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-}
-
-static status_t readProtoFile(Trace* trace) {
-    status_t err = NO_ERROR;
-
-    int fd = open(DEFAULT_FILENAME, O_RDONLY);
-    {
-        google::protobuf::io::FileInputStream f(fd);
-        if (fd && !trace->ParseFromZeroCopyStream(&f)) {
-            err = PERMISSION_DENIED;
-        }
-    }
-    close(fd);
-
-    return err;
-}
-
-static void enableInterceptor() {
-    system("service call SurfaceFlinger 1020 i32 1 > /dev/null");
-}
-
-static void disableInterceptor() {
-    system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
-}
-
-std::string getUniqueName(const std::string& name, const Increment& increment) {
-    return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id());
-}
-
-int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
-    int32_t layerId = 0;
-    for (const auto& increment : capturedTrace.increment()) {
-        if (increment.increment_case() == increment.kSurfaceCreation) {
-            if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) {
-                layerId = increment.surface_creation().id();
-            }
-        }
-    }
-    return layerId;
-}
-
-int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) {
-    int32_t displayId = 0;
-    for (const auto& increment : capturedTrace.increment()) {
-        if (increment.increment_case() == increment.kDisplayCreation) {
-            if (increment.display_creation().name() == displayName) {
-                displayId = increment.display_creation().id();
-                break;
-            }
-        }
-    }
-    return displayId;
-}
-
-class SurfaceInterceptorTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        // Allow SurfaceInterceptor write to /data
-        system("setenforce 0");
-
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-    }
-
-    void TearDown() override {
-        mComposerClient->dispose();
-        mBGSurfaceControl.clear();
-        mFGSurfaceControl.clear();
-        mComposerClient.clear();
-        system("setenforce 1");
-    }
-
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-    int32_t mBGLayerId;
-    int32_t mFGLayerId;
-
-public:
-    using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
-    using TestAction = void (SurfaceInterceptorTest::*)();
-    using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&);
-    using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&);
-
-    void setupBackgroundSurface();
-    void preProcessTrace(const Trace& trace);
-
-    // captureTest will enable SurfaceInterceptor, setup background surface,
-    // disable SurfaceInterceptor, collect the trace and process the trace for
-    // id of background surface before further verification.
-    void captureTest(TestTransactionAction action, TestBooleanVerification verification);
-    void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase);
-    void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase);
-    void captureTest(TestAction action, TestBooleanVerification verification);
-    void captureTest(TestAction action, TestVerification verification);
-    void runInTransaction(TestTransactionAction action);
-
-    // Verification of changes to a surface
-    bool positionUpdateFound(const SurfaceChange& change, bool foundPosition);
-    bool sizeUpdateFound(const SurfaceChange& change, bool foundSize);
-    bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
-    bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
-    bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
-    bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
-    bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                         bool foundBackgroundBlurRadius);
-    bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
-    bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
-    bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
-    bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
-    bool layerStackUpdateFound(const SurfaceChange& change, bool foundLayerStack);
-    bool hiddenFlagUpdateFound(const SurfaceChange& change, bool foundHiddenFlag);
-    bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
-    bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
-    bool reparentUpdateFound(const SurfaceChange& change, bool found);
-    bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
-    bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
-    bool trustedOverlayUpdateFound(const SurfaceChange& change, bool found);
-    bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
-
-    // Find all of the updates in the single trace
-    void assertAllUpdatesFound(const Trace& trace);
-
-    // Verification of creation and deletion of a surface
-    bool surfaceCreationFound(const Increment& increment, bool foundSurface);
-    bool surfaceDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundSurface);
-    bool displayCreationFound(const Increment& increment, bool foundDisplay);
-    bool displayDeletionFound(const Increment& increment, const int32_t targetId,
-            bool foundDisplay);
-    bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase);
-
-    // Verification of buffer updates
-    bool bufferUpdatesFound(const Trace& trace);
-
-    // Perform each of the possible changes to a surface
-    void positionUpdate(Transaction&);
-    void sizeUpdate(Transaction&);
-    void alphaUpdate(Transaction&);
-    void layerUpdate(Transaction&);
-    void cropUpdate(Transaction&);
-    void cornerRadiusUpdate(Transaction&);
-    void backgroundBlurRadiusUpdate(Transaction&);
-    void blurRegionsUpdate(Transaction&);
-    void matrixUpdate(Transaction&);
-    void transparentRegionHintUpdate(Transaction&);
-    void layerStackUpdate(Transaction&);
-    void hiddenFlagUpdate(Transaction&);
-    void opaqueFlagUpdate(Transaction&);
-    void secureFlagUpdate(Transaction&);
-    void reparentUpdate(Transaction&);
-    void relativeParentUpdate(Transaction&);
-    void shadowRadiusUpdate(Transaction&);
-    void trustedOverlayUpdate(Transaction&);
-    void surfaceCreation(Transaction&);
-    void displayCreation(Transaction&);
-    void displayDeletion(Transaction&);
-
-    void nBufferUpdates();
-    void runAllUpdates();
-
-private:
-    void captureInTransaction(TestTransactionAction action, Trace*);
-    void capture(TestAction action, Trace*);
-};
-
-void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    runInTransaction(action);
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) {
-    enableInterceptor();
-    setupBackgroundSurface();
-    (this->*action)();
-    disableInterceptor();
-    ASSERT_EQ(NO_ERROR, readProtoFile(outTrace));
-    preProcessTrace(*outTrace);
-}
-
-void SurfaceInterceptorTest::setupBackgroundSurface() {
-    const auto display = SurfaceComposerClient::getInternalDisplayToken();
-    ASSERT_FALSE(display == nullptr);
-
-    ui::DisplayMode mode;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-    const ui::Size& resolution = mode.resolution;
-
-    // Background surface
-    mBGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mBGSurfaceControl != nullptr);
-    ASSERT_TRUE(mBGSurfaceControl->isValid());
-
-    // Foreground surface
-    mFGSurfaceControl =
-            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
-                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mFGSurfaceControl != nullptr);
-    ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-    Transaction t;
-    t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-    ASSERT_EQ(NO_ERROR,
-              t.setLayer(mBGSurfaceControl, INT_MAX - 3)
-                      .show(mBGSurfaceControl)
-                      .setLayer(mFGSurfaceControl, INT_MAX - 3)
-                      .show(mFGSurfaceControl)
-                      .apply());
-}
-
-void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
-    mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        TestBooleanVerification verification) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        Increment::IncrementCase incrementCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    Trace capturedTrace;
-    captureInTransaction(action, &capturedTrace);
-    ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    ASSERT_TRUE((this->*verification)(capturedTrace));
-}
-
-void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) {
-    Trace capturedTrace;
-    capture(action, &capturedTrace);
-    (this->*verification)(capturedTrace);
-}
-
-void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) {
-    Transaction t;
-    (this->*action)(t);
-    t.apply(true);
-}
-
-void SurfaceInterceptorTest::positionUpdate(Transaction& t) {
-    t.setPosition(mBGSurfaceControl, POSITION_UPDATE, POSITION_UPDATE);
-}
-
-void SurfaceInterceptorTest::sizeUpdate(Transaction& t) {
-    t.setSize(mBGSurfaceControl, SIZE_UPDATE, SIZE_UPDATE);
-}
-
-void SurfaceInterceptorTest::alphaUpdate(Transaction& t) {
-    t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
-}
-
-void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
-    t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
-    t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
-    BLUR_REGIONS_UPDATE.empty();
-    BLUR_REGIONS_UPDATE.push_back(BlurRegion());
-    t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
-}
-
-void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
-    t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
-}
-
-void SurfaceInterceptorTest::cropUpdate(Transaction& t) {
-    t.setCrop(mBGSurfaceControl, CROP_UPDATE);
-}
-
-void SurfaceInterceptorTest::matrixUpdate(Transaction& t) {
-    t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
-}
-
-void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
-    Region region(CROP_UPDATE);
-    t.setTransparentRegionHint(mBGSurfaceControl, region);
-}
-
-void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
-    t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
-}
-
-void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
-}
-
-void SurfaceInterceptorTest::opaqueFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
-}
-
-void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) {
-    t.setFlags(mBGSurfaceControl, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
-}
-
-void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
-    t.reparent(mBGSurfaceControl, mFGSurfaceControl);
-}
-
-void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
-    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z);
-}
-
-void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
-    t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
-}
-
-void SurfaceInterceptorTest::trustedOverlayUpdate(Transaction& t) {
-    t.setTrustedOverlay(mBGSurfaceControl, true);
-}
-
-void SurfaceInterceptorTest::displayCreation(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::displayDeletion(Transaction&) {
-    sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
-    SurfaceComposerClient::destroyDisplay(testDisplay);
-}
-
-void SurfaceInterceptorTest::runAllUpdates() {
-    runInTransaction(&SurfaceInterceptorTest::positionUpdate);
-    runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
-    runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerUpdate);
-    runInTransaction(&SurfaceInterceptorTest::cropUpdate);
-    runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
-    runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
-    runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
-    runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
-    runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
-    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
-    runInTransaction(&SurfaceInterceptorTest::trustedOverlayUpdate);
-}
-
-void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
-    mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE,
-            PIXEL_FORMAT_RGBA_8888, 0);
-}
-
-void SurfaceInterceptorTest::nBufferUpdates() {
-    std::random_device rd;
-    std::mt19937_64 gen(rd());
-    // This makes testing fun
-    std::uniform_int_distribution<uint8_t> dis;
-    for (uint32_t i = 0; i < BUFFER_UPDATES; ++i) {
-        fillSurfaceRGBA8(mBGSurfaceControl, dis(gen), dis(gen), dis(gen));
-    }
-}
-
-bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bool foundPosition) {
-    // There should only be one position transaction with x and y = POSITION_UPDATE
-    bool hasX(change.position().x() == POSITION_UPDATE);
-    bool hasY(change.position().y() == POSITION_UPDATE);
-    if (hasX && hasY && !foundPosition) {
-        foundPosition = true;
-    } else if (hasX && hasY && foundPosition) {
-        // Failed because the position update was found a second time
-        [] () { FAIL(); }();
-    }
-    return foundPosition;
-}
-
-bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool foundSize) {
-    bool hasWidth(change.size().h() == SIZE_UPDATE);
-    bool hasHeight(change.size().w() == SIZE_UPDATE);
-    if (hasWidth && hasHeight && !foundSize) {
-        foundSize = true;
-    } else if (hasWidth && hasHeight && foundSize) {
-        [] () { FAIL(); }();
-    }
-    return foundSize;
-}
-
-bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool foundAlpha) {
-    bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE);
-    if (hasAlpha && !foundAlpha) {
-        foundAlpha = true;
-    } else if (hasAlpha && foundAlpha) {
-        [] () { FAIL(); }();
-    }
-    return foundAlpha;
-}
-
-bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
-                                                     bool foundCornerRadius) {
-    bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
-    if (hasCornerRadius && !foundCornerRadius) {
-        foundCornerRadius = true;
-    } else if (hasCornerRadius && foundCornerRadius) {
-        [] () { FAIL(); }();
-    }
-    return foundCornerRadius;
-}
-
-bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
-                                                             bool foundBackgroundBlur) {
-    bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
-                           BACKGROUND_BLUR_RADIUS_UPDATE);
-    if (hasBackgroundBlur && !foundBackgroundBlur) {
-        foundBackgroundBlur = true;
-    } else if (hasBackgroundBlur && foundBackgroundBlur) {
-        []() { FAIL(); }();
-    }
-    return foundBackgroundBlur;
-}
-
-bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
-                                                    bool foundBlurRegions) {
-    bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
-    if (hasBlurRegions && !foundBlurRegions) {
-        foundBlurRegions = true;
-    } else if (hasBlurRegions && foundBlurRegions) {
-        []() { FAIL(); }();
-    }
-    return foundBlurRegions;
-}
-
-bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
-    bool hasLayer(change.layer().layer() == LAYER_UPDATE);
-    if (hasLayer && !foundLayer) {
-        foundLayer = true;
-    } else if (hasLayer && foundLayer) {
-        [] () { FAIL(); }();
-    }
-    return foundLayer;
-}
-
-bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool foundCrop) {
-    bool hasLeft(change.crop().rectangle().left() == CROP_UPDATE.left);
-    bool hasTop(change.crop().rectangle().top() == CROP_UPDATE.top);
-    bool hasRight(change.crop().rectangle().right() == CROP_UPDATE.right);
-    bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) {
-        foundCrop = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) {
-        [] () { FAIL(); }();
-    }
-    return foundCrop;
-}
-
-bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) {
-    bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2);
-    bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2);
-    bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2);
-    bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2);
-    if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) {
-        foundMatrix = true;
-    } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) {
-        [] () { FAIL(); }();
-    }
-    return foundMatrix;
-}
-
-bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
-        bool foundTransparentRegion) {
-    auto traceRegion = change.transparent_region_hint().region(0);
-    bool hasLeft(traceRegion.left() == CROP_UPDATE.left);
-    bool hasTop(traceRegion.top() == CROP_UPDATE.top);
-    bool hasRight(traceRegion.right() == CROP_UPDATE.right);
-    bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom);
-    if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) {
-        foundTransparentRegion = true;
-    } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) {
-        [] () { FAIL(); }();
-    }
-    return foundTransparentRegion;
-}
-
-bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change,
-        bool foundLayerStack) {
-    bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE);
-    if (hasLayerStackUpdate && !foundLayerStack) {
-        foundLayerStack = true;
-    } else if (hasLayerStackUpdate && foundLayerStack) {
-        [] () { FAIL(); }();
-    }
-    return foundLayerStack;
-}
-
-bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change,
-        bool foundHiddenFlag) {
-    bool hasHiddenFlag(change.hidden_flag().hidden_flag());
-    if (hasHiddenFlag && !foundHiddenFlag) {
-        foundHiddenFlag = true;
-    } else if (hasHiddenFlag && foundHiddenFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundHiddenFlag;
-}
-
-bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change,
-        bool foundOpaqueFlag) {
-    bool hasOpaqueFlag(change.opaque_flag().opaque_flag());
-    if (hasOpaqueFlag && !foundOpaqueFlag) {
-        foundOpaqueFlag = true;
-    } else if (hasOpaqueFlag && foundOpaqueFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundOpaqueFlag;
-}
-
-bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change,
-        bool foundSecureFlag) {
-    bool hasSecureFlag(change.secure_flag().secure_flag());
-    if (hasSecureFlag && !foundSecureFlag) {
-        foundSecureFlag = true;
-    } else if (hasSecureFlag && foundSecureFlag) {
-        [] () { FAIL(); }();
-    }
-    return foundSecureFlag;
-}
-
-bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.reparent().parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
-    bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
-    if (hasId && !found) {
-        found = true;
-    } else if (hasId && found) {
-        []() { FAIL(); }();
-    }
-    return found;
-}
-
-bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
-                                                     bool foundShadowRadius) {
-    bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
-    if (hasShadowRadius && !foundShadowRadius) {
-        foundShadowRadius = true;
-    } else if (hasShadowRadius && foundShadowRadius) {
-        []() { FAIL(); }();
-    }
-    return foundShadowRadius;
-}
-
-bool SurfaceInterceptorTest::trustedOverlayUpdateFound(const SurfaceChange& change,
-                                                       bool foundTrustedOverlay) {
-    bool hasTrustedOverlay(change.trusted_overlay().is_trusted_overlay());
-    if (hasTrustedOverlay && !foundTrustedOverlay) {
-        foundTrustedOverlay = true;
-    } else if (hasTrustedOverlay && foundTrustedOverlay) {
-        []() { FAIL(); }();
-    }
-    return foundTrustedOverlay;
-}
-
-bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
-        SurfaceChange::SurfaceChangeCase changeCase) {
-    bool foundUpdate = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == increment.kTransaction) {
-            for (const auto& change : increment.transaction().surface_change()) {
-                if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) {
-                    switch (changeCase) {
-                        case SurfaceChange::SurfaceChangeCase::kPosition:
-                            // foundUpdate is sent for the tests to fail on duplicated increments
-                            foundUpdate = positionUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSize:
-                            foundUpdate = sizeUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kAlpha:
-                            foundUpdate = alphaUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayer:
-                            foundUpdate = layerUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCrop:
-                            foundUpdate = cropUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kCornerRadius:
-                            foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
-                            foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kBlurRegions:
-                            foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kMatrix:
-                            foundUpdate = matrixUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
-                            foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kLayerStack:
-                            foundUpdate = layerStackUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
-                            foundUpdate = hiddenFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
-                            foundUpdate = opaqueFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kSecureFlag:
-                            foundUpdate = secureFlagUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kReparent:
-                            foundUpdate = reparentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kRelativeParent:
-                            foundUpdate = relativeParentUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kShadowRadius:
-                            foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::kTrustedOverlay:
-                            foundUpdate = trustedOverlayUpdateFound(change, foundUpdate);
-                            break;
-                        case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
-                            break;
-                    }
-                }
-            }
-        }
-    }
-    return foundUpdate;
-}
-
-void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) {
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
-    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
-}
-
-bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
-    bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundSurface) {
-    bool isMatch(increment.surface_deletion().id() == targetId);
-    if (isMatch && !foundSurface) {
-        foundSurface = true;
-    } else if (isMatch && foundSurface) {
-        [] () { FAIL(); }();
-    }
-    return foundSurface;
-}
-
-bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) {
-    bool isMatch(increment.display_creation().name() == DISPLAY_NAME.c_str() &&
-                 !increment.display_creation().is_secure());
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment,
-        const int32_t targetId, bool foundDisplay) {
-    bool isMatch(increment.display_deletion().id() == targetId);
-    if (isMatch && !foundDisplay) {
-        foundDisplay = true;
-    } else if (isMatch && foundDisplay) {
-        [] () { FAIL(); }();
-    }
-    return foundDisplay;
-}
-
-bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace,
-        Increment::IncrementCase incrementCase) {
-    bool foundIncrement = false;
-    for (const auto& increment : trace.increment()) {
-        if (increment.increment_case() == incrementCase) {
-            int32_t targetId = 0;
-            switch (incrementCase) {
-                case Increment::IncrementCase::kSurfaceCreation:
-                    foundIncrement = surfaceCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kSurfaceDeletion:
-                    // Find the id of created surface.
-                    targetId = getSurfaceId(trace, LAYER_NAME);
-                    foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayCreation:
-                    foundIncrement = displayCreationFound(increment, foundIncrement);
-                    break;
-                case Increment::IncrementCase::kDisplayDeletion:
-                    // Find the id of created display.
-                    targetId = getDisplayId(trace, DISPLAY_NAME.c_str());
-                    foundIncrement = displayDeletionFound(increment, targetId, foundIncrement);
-                    break;
-                default:
-                    /* code */
-                    break;
-            }
-        }
-    }
-    return foundIncrement;
-}
-
-bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) {
-    uint32_t updates = 0;
-    for (const auto& inc : trace.increment()) {
-        if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) {
-            updates++;
-        }
-    }
-    return updates == BUFFER_UPDATES;
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptPositionUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::positionUpdate,
-            SurfaceChange::SurfaceChangeCase::kPosition);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSizeUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::sizeUpdate, SurfaceChange::SurfaceChangeCase::kSize);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAlphaUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::alphaUpdate, SurfaceChange::SurfaceChangeCase::kAlpha);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerUpdate, SurfaceChange::SurfaceChangeCase::kLayer);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
-            SurfaceChange::SurfaceChangeCase::kCornerRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
-                SurfaceChange::SurfaceChangeCase::kBlurRegions);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::transparentRegionHintUpdate,
-            SurfaceChange::SurfaceChangeCase::kTransparentRegionHint);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptLayerStackUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::layerStackUpdate,
-            SurfaceChange::SurfaceChangeCase::kLayerStack);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptHiddenFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::hiddenFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kHiddenFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptOpaqueFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::opaqueFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kOpaqueFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSecureFlagUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::secureFlagUpdate,
-            SurfaceChange::SurfaceChangeCase::kSecureFlag);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::reparentUpdate,
-                SurfaceChange::SurfaceChangeCase::kReparent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
-                SurfaceChange::SurfaceChangeCase::kRelativeParent);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
-                SurfaceChange::SurfaceChangeCase::kShadowRadius);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptTrustedOverlayUpdateWorks) {
-    captureTest(&SurfaceInterceptorTest::trustedOverlayUpdate,
-                SurfaceChange::SurfaceChangeCase::kTrustedOverlay);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
-    captureTest(&SurfaceInterceptorTest::runAllUpdates,
-                &SurfaceInterceptorTest::assertAllUpdatesFound);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::surfaceCreation,
-            Increment::IncrementCase::kSurfaceCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) {
-    captureTest(&SurfaceInterceptorTest::displayCreation,
-            Increment::IncrementCase::kDisplayCreation);
-}
-
-TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) {
-    enableInterceptor();
-    runInTransaction(&SurfaceInterceptorTest::displayDeletion);
-    disableInterceptor();
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion));
-}
-
-// If the interceptor is enabled while buffer updates are being pushed, the interceptor should
-// first create a snapshot of the existing displays and surfaces and then start capturing
-// the buffer updates
-TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) {
-    setupBackgroundSurface();
-    std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this);
-    enableInterceptor();
-    disableInterceptor();
-    bufferUpdates.join();
-
-    Trace capturedTrace;
-    ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace));
-    const auto& firstIncrement = capturedTrace.mutable_increment(0);
-    ASSERT_EQ(firstIncrement->increment_case(), Increment::IncrementCase::kDisplayCreation);
-}
-}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
new file mode 100644
index 0000000..d0ab105
--- /dev/null
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2023 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 <android/gui/ISurfaceComposerClient.h>
+#include <gtest/gtest.h>
+#include <gui/DisplayCaptureArgs.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+bool operator==(const Color& left, const Color& right) {
+    return left.a == right.a && left.r == right.r && left.g == right.g && left.b == right.b;
+}
+
+class TextureFilteringTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+
+        mParent = createLayer("test-parent", 100, 100,
+                              gui::ISurfaceComposerClient::eFXSurfaceContainer);
+        mLayer = createLayer("test-child", 100, 100,
+                             gui::ISurfaceComposerClient::eFXSurfaceBufferState, mParent.get());
+        sp<GraphicBuffer> buffer =
+                sp<GraphicBuffer>::make(static_cast<uint32_t>(100), static_cast<uint32_t>(100),
+                                        PIXEL_FORMAT_RGBA_8888, 1u,
+                                        BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                                BufferUsage::COMPOSER_OVERLAY |
+                                                BufferUsage::GPU_TEXTURE,
+                                        "test");
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect{0, 0, 50, 100}, Color::RED);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect{50, 0, 100, 100}, Color::BLUE);
+        Transaction()
+                .setBuffer(mLayer, buffer)
+                .setDataspace(mLayer, ui::Dataspace::V0_SRGB)
+                .setLayer(mLayer, INT32_MAX)
+                .apply();
+    }
+
+    virtual void TearDown() { LayerTransactionTest::TearDown(); }
+
+    void expectFiltered(Rect redRect, Rect blueRect) {
+        // Check that at least some of the pixels in the red rectangle aren't solid red
+        int redPixels = 0;
+        for (int x = redRect.left; x < redRect.right; x++) {
+            for (int y = redRect.top; y < redRect.bottom; y++) {
+                redPixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+                                                     static_cast<uint32_t>(y)) == Color::RED;
+            }
+        }
+        ASSERT_LT(redPixels, redRect.getWidth() * redRect.getHeight());
+
+        // Check that at least some of the pixels in the blue rectangle aren't solid blue
+        int bluePixels = 0;
+        for (int x = blueRect.left; x < blueRect.right; x++) {
+            for (int y = blueRect.top; y < blueRect.bottom; y++) {
+                bluePixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+                                                      static_cast<uint32_t>(y)) == Color::BLUE;
+            }
+        }
+        ASSERT_LT(bluePixels, blueRect.getWidth() * blueRect.getHeight());
+    }
+
+    sp<SurfaceControl> mParent;
+    sp<SurfaceControl> mLayer;
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(TextureFilteringTest, NoFiltering) {
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
+    Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+// Expect filtering because the buffer is stretched to the layer's bounds.
+TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
+    Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the output source crop is stretched to the output buffer's size.
+TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer crop and output source crop are stretched to the output
+// buffer's size.
+TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
+    Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 100;
+    captureArgs.height = 100;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer is scaled up.
+TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.frameScaleX = 2;
+    captureArgs.frameScaleY = 2;
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200});
+}
+
+// Expect no filtering because the output buffer's size matches the source crop.
+TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output buffer's size matches the source crop (with a cropped
+// layer).
+TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
+    Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mLayer->getHandle();
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output source crop and output buffer are the same size.
+TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    captureArgs.width = 50;
+    captureArgs.height = 50;
+    captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+    mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
+    Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+    mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+// Expect no filtering because the parent layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
+    Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
+
+    gui::DisplayCaptureArgs captureArgs;
+    captureArgs.displayToken = mDisplay;
+    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+    mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+// Expect no filtering because parent's position transform shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
+    Transaction().setPosition(mParent, 100, 100).apply();
+
+    LayerCaptureArgs captureArgs;
+    captureArgs.layerHandle = mParent->getHandle();
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+    mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+    mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 8ce63bc..797a64c 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -35,7 +35,10 @@
                 return mDelegate->screenshot();
             case RenderPath::VIRTUAL_DISPLAY:
 
-                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+                const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+                const auto displayToken = ids.empty()
+                        ? nullptr
+                        : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
 
                 ui::DisplayState displayState;
                 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
@@ -53,11 +56,11 @@
                 consumer->setConsumerName(String8("Virtual disp consumer"));
                 consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
 
-                itemConsumer = new BufferItemConsumer(consumer,
-                                                      // Sample usage bits from screenrecord
-                                                      GRALLOC_USAGE_HW_VIDEO_ENCODER |
-                                                              GRALLOC_USAGE_SW_READ_OFTEN);
-                sp<BufferListener> listener = new BufferListener(this);
+                itemConsumer = sp<BufferItemConsumer>::make(consumer,
+                                                            // Sample usage bits from screenrecord
+                                                            GRALLOC_USAGE_HW_VIDEO_ENCODER |
+                                                                    GRALLOC_USAGE_SW_READ_OFTEN);
+                sp<BufferListener> listener = sp<BufferListener>::make(this);
                 itemConsumer->setFrameAvailableListener(listener);
 
                 vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index 18e0806..f31f582 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -33,7 +33,7 @@
         consumer->setConsumerName(String8("Virtual disp consumer"));
         consumer->setDefaultBufferSize(100, 100);
 
-        mGLConsumer = new GLConsumer(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
+        mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
     }
 
     sp<IGraphicBufferProducer> mProducer;
@@ -55,7 +55,7 @@
     // add another sync since we are deferring the display destruction
     t.apply(true);
 
-    sp<Surface> surface = new Surface(mProducer);
+    sp<Surface> surface = sp<Surface>::make(mProducer);
     sp<ANativeWindow> window(surface);
 
     ASSERT_EQ(NO_ERROR, native_window_api_connect(window.get(), NATIVE_WINDOW_API_EGL));
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index bb52245..ad9a674 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -16,68 +16,40 @@
 
 #include <gtest/gtest.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfosUpdate.h>
 #include <private/android_filesystem_config.h>
+#include <cstdint>
 #include <future>
-#include "utils/TransactionUtils.h"
+#include "utils/WindowInfosListenerUtils.h"
 
 namespace android {
 using Transaction = SurfaceComposerClient::Transaction;
 using gui::DisplayInfo;
 using gui::WindowInfo;
+constexpr auto findMatchingWindowInfo = WindowInfosListenerUtils::findMatchingWindowInfo;
+
+using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
 
 class WindowInfosListenerTest : public ::testing::Test {
 protected:
     void SetUp() override {
         seteuid(AID_SYSTEM);
-        mClient = new SurfaceComposerClient;
-        mWindowInfosListener = new SyncWindowInfosListener();
-        mClient->addWindowInfosListener(mWindowInfosListener);
+        mClient = sp<SurfaceComposerClient>::make();
     }
 
-    void TearDown() override {
-        mClient->removeWindowInfosListener(mWindowInfosListener);
-        seteuid(AID_ROOT);
-    }
-
-    struct SyncWindowInfosListener : public gui::WindowInfosListener {
-    public:
-        void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
-                                  const std::vector<DisplayInfo>&) override {
-            windowInfosPromise.set_value(windowInfos);
-        }
-
-        std::vector<WindowInfo> waitForWindowInfos() {
-            std::future<std::vector<WindowInfo>> windowInfosFuture =
-                    windowInfosPromise.get_future();
-            std::vector<WindowInfo> windowInfos = windowInfosFuture.get();
-            windowInfosPromise = std::promise<std::vector<WindowInfo>>();
-            return windowInfos;
-        }
-
-    private:
-        std::promise<std::vector<WindowInfo>> windowInfosPromise;
-    };
+    void TearDown() override { seteuid(AID_ROOT); }
 
     sp<SurfaceComposerClient> mClient;
-    sp<SyncWindowInfosListener> mWindowInfosListener;
-};
+    WindowInfosListenerUtils mWindowInfosListenerUtils;
 
-std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo,
-                                                 std::vector<WindowInfo> windowInfos) {
-    std::optional<WindowInfo> foundWindowInfo = std::nullopt;
-    for (WindowInfo windowInfo : windowInfos) {
-        if (windowInfo.token == targetWindowInfo.token) {
-            foundWindowInfo = std::make_optional<>(windowInfo);
-            break;
-        }
+    bool waitForWindowInfosPredicate(const WindowInfosPredicate& predicate) {
+        return mWindowInfosListenerUtils.waitForWindowInfosPredicate(std::move(predicate));
     }
-
-    return foundWindowInfo;
-}
+};
 
 TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) {
     std::string name = "Test Layer";
-    sp<IBinder> token = new BBinder();
+    sp<IBinder> token = sp<BBinder>::make();
     WindowInfo windowInfo;
     windowInfo.name = name;
     windowInfo.token = token;
@@ -92,20 +64,22 @@
             .setInputWindowInfo(surfaceControl, windowInfo)
             .apply();
 
-    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
-    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
-    ASSERT_NE(std::nullopt, foundWindowInfo);
+    auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+        return findMatchingWindowInfo(windowInfo, windowInfos);
+    };
+    ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent));
 
     Transaction().reparent(surfaceControl, nullptr).apply();
 
-    windowInfos = mWindowInfosListener->waitForWindowInfos();
-    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
-    ASSERT_EQ(std::nullopt, foundWindowInfo);
+    auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) {
+        return !findMatchingWindowInfo(windowInfo, windowInfos);
+    };
+    ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent));
 }
 
 TEST_F(WindowInfosListenerTest, WindowInfoChanged) {
     std::string name = "Test Layer";
-    sp<IBinder> token = new BBinder();
+    sp<IBinder> token = sp<BBinder>::make();
     WindowInfo windowInfo;
     windowInfo.name = name;
     windowInfo.token = token;
@@ -121,19 +95,30 @@
             .setInputWindowInfo(surfaceControl, windowInfo)
             .apply();
 
-    std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos();
-    std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
-    ASSERT_NE(std::nullopt, foundWindowInfo);
-    ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty());
+    auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) {
+        auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+        if (!foundWindowInfo) {
+            return false;
+        }
+        return foundWindowInfo->touchableRegion.isEmpty();
+    };
+    ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty));
 
-    Rect touchableRegions(0, 0, 50, 50);
-    windowInfo.addTouchableRegion(Rect(0, 0, 50, 50));
+    windowInfo.addTouchableRegion({0, 0, 50, 50});
     Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply();
 
-    windowInfos = mWindowInfosListener->waitForWindowInfos();
-    foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
-    ASSERT_NE(std::nullopt, foundWindowInfo);
-    ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion));
+    auto windowIsPresentAndTouchableRegionMatches =
+            [&](const std::vector<WindowInfo>& windowInfos) {
+                auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos);
+                if (!foundWindowInfo) {
+                    return false;
+                }
+
+                auto touchableRegion =
+                        foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion);
+                return touchableRegion.hasSameRects(windowInfo.touchableRegion);
+            };
+    ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches));
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
deleted file mode 100644
index 704815d..0000000
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_native_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_test {
-    name: "sffakehwc_test",
-    defaults: ["surfaceflinger_defaults"],
-    test_suites: ["device-tests"],
-    srcs: [
-        "FakeComposerClient.cpp",
-        "FakeComposerService.cpp",
-        "FakeComposerUtils.cpp",
-        "SFFakeHwc_test.cpp",
-    ],
-    require_root: true,
-    shared_libs: [
-        "android.hardware.graphics.composer@2.1",
-        "android.hardware.graphics.composer@2.2",
-        "android.hardware.graphics.composer@2.3",
-        "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer3-V1-ndk",
-        "android.hardware.graphics.mapper@2.0",
-        "android.hardware.graphics.mapper@3.0",
-        "android.hardware.graphics.mapper@4.0",
-        "android.hardware.power@1.3",
-        "android.hardware.power-V2-cpp",
-        "libbase",
-        "libbinder",
-        "libbinder_ndk",
-        "libcutils",
-        "libfmq",
-        "libgui",
-        "libhidlbase",
-        "liblayers_proto",
-        "liblog",
-        "libnativewindow",
-        "libsync",
-        "libtimestats",
-        "libui",
-        "libutils",
-    ],
-    static_libs: [
-        "android.hardware.graphics.composer@2.1-resources",
-        "libaidlcommonsupport",
-        "libcompositionengine",
-        "libgmock",
-        "libperfetto_client_experimental",
-        "librenderengine",
-        "libtrace_proto",
-        "libaidlcommonsupport",
-    ],
-    header_libs: [
-        "android.hardware.graphics.composer@2.4-command-buffer",
-        "android.hardware.graphics.composer@2.4-hal",
-        "android.hardware.graphics.composer3-command-buffer",
-        "libsurfaceflinger_headers",
-    ],
-}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
deleted file mode 100644
index b38032d..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ /dev/null
@@ -1,927 +0,0 @@
-/*
- * Copyright (C) 2017 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"
-
-//#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeComposer"
-
-#include "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-
-#include <log/log.h>
-
-#include <gtest/gtest.h>
-
-#include <inttypes.h>
-#include <time.h>
-#include <algorithm>
-#include <condition_variable>
-#include <iostream>
-#include <mutex>
-#include <set>
-#include <thread>
-
-constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
-
-using namespace sftest;
-
-using android::Condition;
-using android::Mutex;
-
-using Clock = std::chrono::steady_clock;
-using TimePoint = std::chrono::time_point<Clock>;
-
-namespace {
-
-// Internal state of a layer in the HWC API.
-class LayerImpl {
-public:
-    LayerImpl() = default;
-
-    bool mValid = true;
-    RenderState mRenderState;
-    uint32_t mZ = 0;
-};
-
-// Struct for storing per frame rectangle state. Contains the render
-// state shared to the test case. Basically a snapshot and a subset of
-// LayerImpl sufficient to re-create the pixels of a layer for the
-// frame.
-struct FrameRect {
-public:
-    FrameRect(Layer layer_, const RenderState& state, uint32_t z_)
-          : layer(layer_), renderState(state), z(z_) {}
-
-    const Layer layer;
-    const RenderState renderState;
-    const uint32_t z;
-};
-
-// Collection of FrameRects forming one rendered frame. Could store
-// related fences and other data in the future.
-class Frame {
-public:
-    Frame() = default;
-    std::vector<std::unique_ptr<FrameRect>> rectangles;
-};
-
-class DelayedEventGenerator {
-public:
-    explicit DelayedEventGenerator(std::function<void()> onTimerExpired)
-          : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {}
-
-    ~DelayedEventGenerator() {
-        ALOGI("DelayedEventGenerator exiting.");
-        {
-            std::unique_lock<std::mutex> lock(mMutex);
-            mRunning = false;
-            mWakeups.clear();
-            mCondition.notify_one();
-        }
-        mThread.join();
-        ALOGI("DelayedEventGenerator exited.");
-    }
-
-    void wakeAfter(std::chrono::nanoseconds waitTime) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mWakeups.insert(Clock::now() + waitTime);
-        mCondition.notify_one();
-    }
-
-private:
-    void loop() {
-        while (true) {
-            // Lock scope
-            {
-                std::unique_lock<std::mutex> lock(mMutex);
-                mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); });
-                if (!mRunning && mWakeups.empty()) {
-                    // This thread should only exit once the destructor has been called and all
-                    // wakeups have been processed
-                    return;
-                }
-
-                // At this point, mWakeups will not be empty
-
-                TimePoint target = *(mWakeups.begin());
-                auto status = mCondition.wait_until(lock, target);
-                while (status == std::cv_status::no_timeout) {
-                    // This was either a spurious wakeup or another wakeup was added, so grab the
-                    // oldest point and wait again
-                    target = *(mWakeups.begin());
-                    status = mCondition.wait_until(lock, target);
-                }
-
-                // status must have been timeout, so we can finally clear this point
-                mWakeups.erase(target);
-            }
-            // Callback *without* locks!
-            mOnTimerExpired();
-        }
-    }
-
-    std::function<void()> mOnTimerExpired;
-    std::thread mThread;
-    std::mutex mMutex;
-    std::condition_variable mCondition;
-    bool mRunning = true;
-    std::set<TimePoint> mWakeups;
-};
-
-} // namespace
-
-FakeComposerClient::FakeComposerClient()
-      : mEventCallback(nullptr),
-        mEventCallback_2_4(nullptr),
-        mCurrentConfig(NULL_DISPLAY_CONFIG),
-        mVsyncEnabled(false),
-        mLayers(),
-        mDelayedEventGenerator(
-                std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })),
-        mSurfaceComposer(nullptr) {}
-
-FakeComposerClient::~FakeComposerClient() {}
-
-bool FakeComposerClient::hasCapability(hwc2_capability_t /*capability*/) {
-    return false;
-}
-
-std::string FakeComposerClient::dumpDebugInfo() {
-    return {};
-}
-
-void FakeComposerClient::registerEventCallback(EventCallback* callback) {
-    ALOGV("registerEventCallback");
-    LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
-                 "already registered using registerEventCallback_2_4");
-
-    mEventCallback = callback;
-    if (mEventCallback) {
-        mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
-    }
-}
-
-void FakeComposerClient::unregisterEventCallback() {
-    ALOGV("unregisterEventCallback");
-    mEventCallback = nullptr;
-}
-
-void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
-    if (mEventCallback) {
-        mEventCallback->onHotplug(display, state);
-    } else if (mEventCallback_2_4) {
-        mEventCallback_2_4->onHotplug(display, state);
-    }
-}
-
-void FakeComposerClient::refreshDisplay(Display display) {
-    if (mEventCallback) {
-        mEventCallback->onRefresh(display);
-    } else if (mEventCallback_2_4) {
-        mEventCallback_2_4->onRefresh(display);
-    }
-}
-
-uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
-    ALOGV("getMaxVirtualDisplayCount");
-    return 1;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
-                                                     V1_0::PixelFormat* /*format*/,
-                                                     Display* /*outDisplay*/) {
-    ALOGV("createVirtualDisplay");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
-    ALOGV("destroyVirtualDisplay");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
-    ALOGV("createLayer");
-    *outLayer = mLayers.size();
-    auto newLayer = std::make_unique<LayerImpl>();
-    mLayers.push_back(std::move(newLayer));
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
-    ALOGV("destroyLayer");
-    mLayers[layer]->mValid = false;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
-    ALOGV("getActiveConfig");
-    if (mMockHal) {
-        return mMockHal->getActiveConfig(display, outConfig);
-    }
-
-    // TODO Assert outConfig != nullptr
-
-    // TODO This is my reading of the
-    // IComposerClient::getActiveConfig, but returning BAD_CONFIG
-    // seems to not fit SurfaceFlinger plans. See version 2 below.
-    // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
-    //     return V2_1::Error::BAD_CONFIG;
-    // }
-    //*outConfig = mCurrentConfig;
-    *outConfig = 1; // Very special config for you my friend
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
-                                                       uint32_t /*height*/,
-                                                       V1_0::PixelFormat /*format*/,
-                                                       V1_0::Dataspace /*dataspace*/) {
-    ALOGV("getClientTargetSupport");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
-                                              hidl_vec<V1_0::ColorMode>* /*outModes*/) {
-    ALOGV("getColorModes");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
-                                                    V2_1::IComposerClient::Attribute attribute,
-                                                    int32_t* outValue) {
-    auto tmpError =
-            getDisplayAttribute_2_4(display, config,
-                                    static_cast<IComposerClient::Attribute>(attribute), outValue);
-    return static_cast<V2_1::Error>(tmpError);
-}
-
-V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
-    ALOGV("getDisplayConfigs");
-    if (mMockHal) {
-        return mMockHal->getDisplayConfigs(display, outConfigs);
-    }
-
-    // TODO assert display == 1, outConfigs != nullptr
-
-    outConfigs->resize(1);
-    (*outConfigs)[0] = 1;
-
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
-    ALOGV("getDisplayName");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
-                                               IComposerClient::DisplayType* outType) {
-    ALOGV("getDisplayType");
-    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
-    // assumed to be physical?
-    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
-    ALOGV("getDozeSupport");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
-                                                   hidl_vec<V1_0::Hdr>* /*outTypes*/,
-                                                   float* /*outMaxLuminance*/,
-                                                   float* /*outMaxAverageLuminance*/,
-                                                   float* /*outMinLuminance*/) {
-    ALOGV("getHdrCapabilities");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
-    ALOGV("setActiveConfig");
-    if (mMockHal) {
-        return mMockHal->setActiveConfig(display, config);
-    }
-    mCurrentConfig = config;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
-    ALOGV("setColorMode");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
-                                             V2_1::IComposerClient::PowerMode /*mode*/) {
-    ALOGV("setPowerMode");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
-                                                IComposerClient::Vsync enabled) {
-    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
-    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
-                                                  int32_t /*hint*/) {
-    ALOGV("setColorTransform");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
-                                                int32_t /*acquireFence*/, int32_t /*dataspace*/,
-                                                const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setClientTarget");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
-                                                int32_t /*releaseFence*/) {
-    ALOGV("setOutputBuffer");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::validateDisplay(
-        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
-        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
-        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/) {
-    ALOGV("validateDisplay");
-    // TODO: Assume touching nothing means All Korrekt!
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
-    ALOGV("acceptDisplayChanges");
-    // Didn't ask for changes because software is omnipotent.
-    return V2_1::Error::NONE;
-}
-
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
-    return a->z <= b->z;
-}
-
-V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
-                                               std::vector<Layer>* /*outLayers*/,
-                                               std::vector<int32_t>* /*outReleaseFences*/) {
-    ALOGV("presentDisplay");
-    // TODO Leaving layers and their fences out for now. Doing so
-    // means that we've already processed everything. Important to
-    // test that the fences are respected, though. (How?)
-
-    std::unique_ptr<Frame> newFrame(new Frame);
-    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
-        const LayerImpl& layerImpl = *mLayers[layer];
-
-        if (!layerImpl.mValid) continue;
-
-        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
-        newFrame->rectangles.push_back(std::move(rect));
-    }
-    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
-    {
-        Mutex::Autolock _l(mStateMutex);
-        mFrames.push_back(std::move(newFrame));
-        mFramesAvailable.broadcast();
-    }
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
-                                                       int32_t /*x*/, int32_t /*y*/) {
-    ALOGV("setLayerCursorPosition");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
-                                               buffer_handle_t buffer, int32_t acquireFence) {
-    ALOGV("setLayerBuffer");
-    LayerImpl& l = getLayerImpl(layer);
-    if (buffer != l.mRenderState.mBuffer) {
-        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
-    }
-    l.mRenderState.mBuffer = buffer;
-    l.mRenderState.mAcquireFence = acquireFence;
-
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
-                                                      const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setLayerSurfaceDamage");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
-    ALOGV("setLayerBlendMode");
-    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
-                                              IComposerClient::Color color) {
-    ALOGV("setLayerColor");
-    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
-    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
-    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
-    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
-                                                        int32_t /*type*/) {
-    ALOGV("setLayerCompositionType");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
-                                                  int32_t /*dataspace*/) {
-    ALOGV("setLayerDataspace");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
-                                                     const hwc_rect_t& frame) {
-    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
-          frame.bottom);
-    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
-    ALOGV("setLayerPlaneAlpha");
-    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
-                                                       buffer_handle_t /*stream*/) {
-    ALOGV("setLayerSidebandStream");
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
-                                                   const hwc_frect_t& crop) {
-    ALOGV("setLayerSourceCrop");
-    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
-                                                  int32_t transform) {
-    ALOGV("setLayerTransform");
-    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
-                                                      const std::vector<hwc_rect_t>& visible) {
-    ALOGV("setLayerVisibleRegion");
-    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
-    return V2_1::Error::NONE;
-}
-
-V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
-    ALOGV("setLayerZOrder");
-    getLayerImpl(layer).mZ = z;
-    return V2_1::Error::NONE;
-}
-
-// Composer 2.2
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
-        Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
-        Display /*display*/, Layer /*layer*/,
-        const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
-        Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
-        graphics::common::V1_1::Dataspace* /*outDataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
-                                                  const native_handle_t* /*bufferHandle*/,
-                                                  android::base::unique_fd /*fenceFd*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
-                                                       android::base::unique_fd* /*outFenceFd*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
-        uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
-        Display* /*outDisplay*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
-        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
-        graphics::common::V1_1::PixelFormat /*format*/,
-        graphics::common::V1_1::Dataspace /*dataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
-                                                 V2_2::IComposerClient::PowerMode /*mode*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
-                                                   V2_2::IComposerClient::FloatColor /*color*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_2(
-        Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents(
-        Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
-        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
-                                                 graphics::common::V1_1::ColorMode /*mode*/,
-                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
-        graphics::common::V1_1::Dataspace /*dataspace*/) {
-    return {};
-}
-
-// Composer 2.3
-V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
-        Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
-                                                 graphics::common::V1_2::ColorMode /*mode*/,
-                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getRenderIntents_2_3(
-        Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
-        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getColorModes_2_3(
-        Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
-        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
-        graphics::common::V1_2::PixelFormat /*format*/,
-        graphics::common::V1_2::Dataspace /*dataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
-        Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
-        graphics::common::V1_2::Dataspace* /*outDataspace*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
-        Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
-        float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
-        Display /*display*/, Layer /*layer*/,
-        const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
-                                                             uint8_t* /*outPort*/,
-                                                             std::vector<uint8_t>* /*outData*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
-                                                       const float* /*matrix*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
-        uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
-        graphics::common::V1_2::Dataspace& /*dataspace*/,
-        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
-        uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
-        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
-        uint64_t /*maxFrames*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayedContentSample(
-        uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
-        uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
-        hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
-        hidl_vec<uint64_t>& /*sampleComponent3*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayCapabilities(
-        Display /*display*/,
-        std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
-        Display /*display*/, Layer /*layer*/,
-        std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
-                                                            bool* /*outSupport*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
-    return V2_1::Error::UNSUPPORTED;
-}
-
-// Composer 2.4
-void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
-    ALOGV("registerEventCallback_2_4");
-    LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
-
-    mEventCallback_2_4 = callback;
-    if (mEventCallback_2_4) {
-        mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
-    }
-}
-
-void FakeComposerClient::unregisterEventCallback_2_4() {
-    ALOGV("unregisterEventCallback_2_4");
-    mEventCallback_2_4 = nullptr;
-}
-
-V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
-        Display /*display*/,
-        std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayConnectionType(
-        Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
-                                                        IComposerClient::Attribute attribute,
-                                                        int32_t* outValue) {
-    ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
-          static_cast<int>(config), static_cast<int>(attribute), outValue);
-    if (mMockHal) {
-        return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
-    }
-
-    // TODO: SOOO much fun to be had with these alone
-    switch (attribute) {
-        case IComposerClient::Attribute::WIDTH:
-            *outValue = 1920;
-            break;
-        case IComposerClient::Attribute::HEIGHT:
-            *outValue = 1080;
-            break;
-        case IComposerClient::Attribute::VSYNC_PERIOD:
-            *outValue = 1666666666;
-            break; // TOOD: Tests break down if lowered to 16ms?
-        case IComposerClient::Attribute::DPI_X:
-            *outValue = 240;
-            break;
-        case IComposerClient::Attribute::DPI_Y:
-            *outValue = 240;
-            break;
-        default:
-            LOG_ALWAYS_FATAL("Say what!?! New attribute");
-    }
-
-    return Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
-                                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) {
-    ALOGV("getDisplayVsyncPeriod");
-    if (mMockHal) {
-        return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
-    }
-
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
-        Display display, Config config,
-        const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-        VsyncPeriodChangeTimeline* timeline) {
-    ALOGV("setActiveConfigWithConstraints");
-    if (mMockHal) {
-        return mMockHal->setActiveConfigWithConstraints(display, config,
-                                                        vsyncPeriodChangeConstraints, timeline);
-    }
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
-    ALOGV("setAutoLowLatencyMode");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getSupportedContentTypes(
-        Display, std::vector<IComposerClient::ContentType>*) {
-    ALOGV("getSupportedContentTypes");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
-    ALOGV("setContentType");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::validateDisplay_2_4(
-        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
-        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
-        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/,
-        IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) {
-    return V2_4::Error::NONE;
-}
-
-V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
-                                                        const std::vector<uint8_t>&) {
-    ALOGV("setLayerGenericMetadata");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys(
-        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
-    ALOGV("getLayerGenericMetadataKeys");
-    return V2_4::Error::UNSUPPORTED;
-}
-
-//////////////////////////////////////////////////////////////////
-
-void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
-    if (mEventCallback || mEventCallback_2_4) {
-        uint64_t timestamp = vsyncTime;
-        ALOGV("Vsync");
-        if (timestamp == 0) {
-            struct timespec ts;
-            clock_gettime(CLOCK_MONOTONIC, &ts);
-            timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
-        }
-        if (mSurfaceComposer != nullptr) {
-            mSurfaceComposer->injectVSync(timestamp);
-        } else if (mEventCallback) {
-            mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
-        } else {
-            mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
-        }
-    }
-}
-
-void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) {
-    mDelayedEventGenerator->wakeAfter(wait);
-}
-
-LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) {
-    // TODO Change these to an internal state check that can be
-    // invoked from the gtest? GTest macros do not seem all that safe
-    // when used outside the test class
-    EXPECT_GE(handle, static_cast<Layer>(0));
-    EXPECT_LT(handle, mLayers.size());
-    return *(mLayers[handle]);
-}
-
-int FakeComposerClient::getFrameCount() const {
-    return mFrames.size();
-}
-
-static std::vector<RenderState> extractRenderState(
-        const std::vector<std::unique_ptr<FrameRect>>& internalRects) {
-    std::vector<RenderState> result;
-    result.reserve(internalRects.size());
-    for (const std::unique_ptr<FrameRect>& rect : internalRects) {
-        result.push_back(rect->renderState);
-    }
-    return result;
-}
-
-std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const {
-    Mutex::Autolock _l(mStateMutex);
-    return extractRenderState(mFrames[frame]->rectangles);
-}
-
-std::vector<RenderState> FakeComposerClient::getLatestFrame() const {
-    Mutex::Autolock _l(mStateMutex);
-    return extractRenderState(mFrames[mFrames.size() - 1]->rectangles);
-}
-
-void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) {
-    int currentFrame = 0;
-    {
-        Mutex::Autolock _l(mStateMutex); // I hope this is ok...
-        currentFrame = static_cast<int>(mFrames.size());
-        requestVSync();
-    }
-    waitUntilFrame(currentFrame + 1, maxWait);
-}
-
-void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const {
-    Mutex::Autolock _l(mStateMutex);
-    while (mFrames.size() < static_cast<size_t>(targetFrame)) {
-        android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count());
-        if (result == android::TIMED_OUT) {
-            ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame,
-                  mFrames.size(), maxWait.count());
-            return;
-        }
-    }
-}
-
-void FakeComposerClient::clearFrames() {
-    Mutex::Autolock _l(mStateMutex);
-    mFrames.clear();
-    for (const std::unique_ptr<LayerImpl>& layer : mLayers) {
-        if (layer->mValid) {
-            layer->mRenderState.mSwapCount = 0;
-        }
-    }
-}
-
-void FakeComposerClient::onSurfaceFlingerStart() {
-    mSurfaceComposer = nullptr;
-    do {
-        mSurfaceComposer = new android::SurfaceComposerClient;
-        android::status_t initResult = mSurfaceComposer->initCheck();
-        if (initResult != android::NO_ERROR) {
-            ALOGD("Init result: %d", initResult);
-            mSurfaceComposer = nullptr;
-            std::this_thread::sleep_for(10ms);
-        }
-    } while (mSurfaceComposer == nullptr);
-    ALOGD("SurfaceComposerClient created");
-    mSurfaceComposer->enableVSyncInjections(true);
-}
-
-void FakeComposerClient::onSurfaceFlingerStop() {
-    mSurfaceComposer->enableVSyncInjections(false);
-    mSurfaceComposer->dispose();
-    mSurfaceComposer.clear();
-}
-
-// Includes destroyed layers, stored in order of creation.
-int FakeComposerClient::getLayerCount() const {
-    return mLayers.size();
-}
-
-Layer FakeComposerClient::getLayer(size_t index) const {
-    // NOTE: If/when passing calls through to actual implementation,
-    // this might get more involving.
-    return static_cast<Layer>(index);
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
deleted file mode 100644
index 600e765..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright 2017 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 <chrono>
-
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-#include <utils/Condition.h>
-
-#include "MockComposerHal.h"
-#include "RenderState.h"
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace {
-class LayerImpl;
-class Frame;
-class DelayedEventGenerator;
-} // namespace
-
-namespace android {
-class SurfaceComposerClient;
-} // namespace android
-
-namespace sftest {
-// NOTE: The ID's need to be exactly these. VR composer and parts of
-// the SurfaceFlinger assume the display IDs to have these values
-// despite the enum being documented as a display type.
-// TODO: Reference to actual documentation
-constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY);
-constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL);
-
-class FakeComposerClient : public ComposerHal {
-public:
-    FakeComposerClient();
-    virtual ~FakeComposerClient();
-
-    void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
-
-    bool hasCapability(hwc2_capability_t capability) override;
-
-    std::string dumpDebugInfo() override;
-    void registerEventCallback(EventCallback* callback) override;
-    void unregisterEventCallback() override;
-
-    uint32_t getMaxVirtualDisplayCount() override;
-    V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
-                                     Display* outDisplay) override;
-    V2_1::Error destroyVirtualDisplay(Display display) override;
-    V2_1::Error createLayer(Display display, Layer* outLayer) override;
-    V2_1::Error destroyLayer(Display display, Layer layer) override;
-
-    V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
-    V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
-                                       V1_0::PixelFormat format,
-                                       V1_0::Dataspace dataspace) override;
-    V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
-    V2_1::Error getDisplayAttribute(Display display, Config config,
-                                    V2_1::IComposerClient::Attribute attribute,
-                                    int32_t* outValue) override;
-    V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-    V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
-    V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
-    V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
-    V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
-                                   float* outMaxLuminance, float* outMaxAverageLuminance,
-                                   float* outMinLuminance) override;
-
-    V2_1::Error setActiveConfig(Display display, Config config) override;
-    V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
-    V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
-    V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
-    V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
-    V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
-                                int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
-    V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
-                                int32_t releaseFence) override;
-    V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
-                                std::vector<IComposerClient::Composition>* outCompositionTypes,
-                                uint32_t* outDisplayRequestMask,
-                                std::vector<Layer>* outRequestedLayers,
-                                std::vector<uint32_t>* outRequestMasks) override;
-    V2_1::Error acceptDisplayChanges(Display display) override;
-    V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
-                               std::vector<Layer>* outLayers,
-                               std::vector<int32_t>* outReleaseFences) override;
-
-    V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
-    V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                               int32_t acquireFence) override;
-    V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
-                                      const std::vector<hwc_rect_t>& damage) override;
-    V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-    V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
-    V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
-    V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
-    V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
-                                     const hwc_rect_t& frame) override;
-    V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-    V2_1::Error setLayerSidebandStream(Display display, Layer layer,
-                                       buffer_handle_t stream) override;
-    V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
-    V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
-    V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
-                                      const std::vector<hwc_rect_t>& visible) override;
-    V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
-    // Composer 2.2
-    V2_1::Error getPerFrameMetadataKeys(
-            Display display,
-            std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
-    V2_1::Error setLayerPerFrameMetadata(
-            Display display, Layer layer,
-            const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
-
-    V2_1::Error getReadbackBufferAttributes(
-            Display display, graphics::common::V1_1::PixelFormat* outFormat,
-            graphics::common::V1_1::Dataspace* outDataspace) override;
-    V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
-                                  android::base::unique_fd fenceFd) override;
-    V2_1::Error getReadbackBufferFence(Display display,
-                                       android::base::unique_fd* outFenceFd) override;
-    V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
-                                         graphics::common::V1_1::PixelFormat* format,
-                                         Display* outDisplay) override;
-    V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
-                                           graphics::common::V1_1::PixelFormat format,
-                                           graphics::common::V1_1::Dataspace dataspace) override;
-    V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
-
-    V2_1::Error setLayerFloatColor(Display display, Layer layer,
-                                   V2_2::IComposerClient::FloatColor color) override;
-
-    V2_1::Error getColorModes_2_2(Display display,
-                                  hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
-    V2_1::Error getRenderIntents(
-            Display display, graphics::common::V1_1::ColorMode mode,
-            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
-    V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
-                                 graphics::common::V1_1::RenderIntent intent) override;
-
-    std::array<float, 16> getDataspaceSaturationMatrix(
-            graphics::common::V1_1::Dataspace dataspace) override;
-
-    // Composer 2.3
-    V2_1::Error getPerFrameMetadataKeys_2_3(
-            Display display,
-            std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
-
-    V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
-                                 graphics::common::V1_1::RenderIntent intent) override;
-
-    V2_1::Error getRenderIntents_2_3(
-            Display display, graphics::common::V1_2::ColorMode mode,
-            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
-
-    V2_1::Error getColorModes_2_3(Display display,
-                                  hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
-
-    V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
-                                           graphics::common::V1_2::PixelFormat format,
-                                           graphics::common::V1_2::Dataspace dataspace) override;
-    V2_1::Error getReadbackBufferAttributes_2_3(
-            Display display, graphics::common::V1_2::PixelFormat* outFormat,
-            graphics::common::V1_2::Dataspace* outDataspace) override;
-    V2_1::Error getHdrCapabilities_2_3(Display display,
-                                       hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
-                                       float* outMaxLuminance, float* outMaxAverageLuminance,
-                                       float* outMinLuminance) override;
-    V2_1::Error setLayerPerFrameMetadata_2_3(
-            Display display, Layer layer,
-            const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
-    V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
-                                             std::vector<uint8_t>* outData) override;
-    V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
-    V2_1::Error getDisplayedContentSamplingAttributes(
-            uint64_t display, graphics::common::V1_2::PixelFormat& format,
-            graphics::common::V1_2::Dataspace& dataspace,
-            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
-    V2_1::Error setDisplayedContentSamplingEnabled(
-            uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
-            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
-            uint64_t maxFrames) override;
-    V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
-                                          uint64_t& frameCount,
-                                          hidl_vec<uint64_t>& sampleComponent0,
-                                          hidl_vec<uint64_t>& sampleComponent1,
-                                          hidl_vec<uint64_t>& sampleComponent2,
-                                          hidl_vec<uint64_t>& sampleComponent3) override;
-    V2_1::Error getDisplayCapabilities(
-            Display display,
-            std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
-    V2_1::Error setLayerPerFrameMetadataBlobs(
-            Display display, Layer layer,
-            std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
-    V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
-    V2_1::Error setDisplayBrightness(Display display, float brightness) override;
-
-    // Composer 2.4
-    void registerEventCallback_2_4(EventCallback_2_4* callback) override;
-
-    void unregisterEventCallback_2_4() override;
-
-    V2_4::Error getDisplayCapabilities_2_4(
-            Display display,
-            std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
-    V2_4::Error getDisplayConnectionType(
-            Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
-    V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
-                                        IComposerClient::Attribute attribute,
-                                        int32_t* outValue) override;
-    V2_4::Error getDisplayVsyncPeriod(Display display,
-                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
-    V2_4::Error setActiveConfigWithConstraints(
-            Display display, Config config,
-            const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
-            VsyncPeriodChangeTimeline* outTimeline) override;
-    V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
-    V2_4::Error getSupportedContentTypes(
-            Display display,
-            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
-    V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
-    V2_4::Error validateDisplay_2_4(
-            Display display, std::vector<Layer>* outChangedLayers,
-            std::vector<IComposerClient::Composition>* outCompositionTypes,
-            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-            std::vector<uint32_t>* outRequestMasks,
-            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
-    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
-                                        bool mandatory, const std::vector<uint8_t>& value) override;
-    V2_4::Error getLayerGenericMetadataKeys(
-            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
-
-    void setClient(ComposerClient* client);
-
-    void requestVSync(uint64_t vsyncTime = 0);
-    // We don't want tests hanging, so always use a timeout. Remember
-    // to always check the number of frames with test ASSERT_!
-    // Wait until next frame is rendered after requesting vsync.
-    void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms);
-    void runVSyncAfter(std::chrono::nanoseconds wait);
-
-    int getFrameCount() const;
-    // We don't want tests hanging, so always use a timeout. Remember
-    // to always check the number of frames with test ASSERT_!
-    void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const;
-    std::vector<RenderState> getFrameRects(int frame) const;
-    std::vector<RenderState> getLatestFrame() const;
-    void clearFrames();
-
-    void onSurfaceFlingerStart();
-    void onSurfaceFlingerStop();
-
-    int getLayerCount() const;
-    Layer getLayer(size_t index) const;
-
-    void hotplugDisplay(Display display, IComposerCallback::Connection state);
-    void refreshDisplay(Display display);
-
-private:
-    LayerImpl& getLayerImpl(Layer handle);
-
-    EventCallback* mEventCallback;
-    EventCallback_2_4* mEventCallback_2_4;
-    Config mCurrentConfig;
-    bool mVsyncEnabled;
-    std::vector<std::unique_ptr<LayerImpl>> mLayers;
-    std::vector<std::unique_ptr<Frame>> mFrames;
-    // Using a pointer to hide the implementation into the CPP file.
-    std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator;
-    android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
-    mutable android::Mutex mStateMutex;
-    mutable android::Condition mFramesAvailable;
-
-    MockComposerHal* mMockHal = nullptr;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
deleted file mode 100644
index c656eed..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2017 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"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcService"
-#include <log/log.h>
-
-#include "FakeComposerService.h"
-
-using namespace android::hardware;
-using namespace android::hardware::graphics::composer;
-
-namespace sftest {
-
-FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_1::~FakeComposerService_2_1() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
-    ALOGI("FakeComposerService::createClient %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_2::~FakeComposerService_2_2() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
-    ALOGI("FakeComposerService::createClient %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_3::~FakeComposerService_2_3() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
-    ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::NONE, mClient);
-    return Void();
-}
-
-FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
-      : mClient(client) {}
-
-FakeComposerService_2_4::~FakeComposerService_2_4() {
-    ALOGI("Maybe killing client %p", mClient.get());
-    // Rely on sp to kill the client.
-}
-
-Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
-    ALOGI("FakeComposerService::getCapabilities");
-    hidl_cb(hidl_vec<Capability>());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
-    ALOGI("FakeComposerService::dumpDebugInfo");
-    hidl_cb(hidl_string());
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
-    LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
-    return Void();
-}
-
-Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
-    ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
-    if (!mClient->init()) {
-        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
-    }
-    hidl_cb(V2_4::Error::NONE, mClient);
-    return Void();
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
deleted file mode 100644
index 47f970f..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 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 <android/hardware/graphics/composer/2.4/IComposer.h>
-#include <composer-hal/2.1/ComposerClient.h>
-#include <composer-hal/2.2/ComposerClient.h>
-#include <composer-hal/2.3/ComposerClient.h>
-#include <composer-hal/2.4/ComposerClient.h>
-
-using android::hardware::Return;
-
-using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
-
-namespace sftest {
-
-using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
-
-class FakeComposerService_2_1 : public IComposer_2_1 {
-public:
-    explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_1();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
-class FakeComposerService_2_2 : public IComposer_2_2 {
-public:
-    explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_2();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
-class FakeComposerService_2_3 : public IComposer_2_3 {
-public:
-    explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_3();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
-
-class FakeComposerService_2_4 : public IComposer_2_4 {
-public:
-    explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService_2_4();
-
-    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
-    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
-    Return<void> createClient(createClient_cb hidl_cb) override;
-    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
-    Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
-
-private:
-    android::sp<ComposerClient> mClient;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
deleted file mode 100644
index 1cea25a..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright 2017 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"
-
-#define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcUtil"
-#include <log/log.h>
-
-#include "FakeComposerUtils.h"
-#include "RenderState.h"
-
-#include "SurfaceFlinger.h" // Get the name of the service...
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <hidl/ServiceManagement.h>
-
-#include <iomanip>
-#include <thread>
-
-using android::String16;
-using android::sp;
-using namespace std::chrono_literals;
-using namespace sftest;
-using std::setw;
-
-namespace sftest {
-
-// clang-format off
-inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) {
-    os << std::fixed << std::setprecision(1) << "("
-       << setw(align) << sourceRect.left << setw(0) << ","
-       << setw(align) << sourceRect.top << setw(0) << ","
-       << setw(align) << sourceRect.right << setw(0) << ","
-       << setw(align) << sourceRect.bottom << setw(0) << ")";
-}
-
-inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) {
-    os << "("
-       << setw(align) << displayRect.left << setw(0) << ","
-       << setw(align) << displayRect.top << setw(0) << ","
-       << setw(align) << displayRect.right << setw(0) << ","
-       << setw(align) << displayRect.bottom << setw(0) << ")";
-}
-// clang-format on
-
-inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) {
-    printSourceRectAligned(os, state.mSourceCrop, 7);
-    os << "->";
-    printDisplayRectAligned(os, state.mDisplayFrame, 5);
-    return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3)
-              << state.mPlaneAlpha << " Xform:" << state.mTransform;
-}
-
-// Helper for verifying the parts of the RenderState
-template <typename T>
-bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val,
-                 const char* name) {
-    if (ref != val) {
-        message = message << "Expected " << name << ":" << ref << ", got:" << val << ".";
-        return false;
-    }
-    return true;
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) {
-    // TODO: Message could start as success and be assigned as failure.
-    // Only problem is that utility assumes it to be failure and just adds stuff. Would
-    // need still special case the initial failure in the utility?
-    // TODO: ... or would it be possible to break this back to gtest primitives?
-    ::testing::AssertionResult message = ::testing::AssertionFailure();
-    bool passes = true;
-
-    // The work here is mostly about providing good log strings for differences
-    passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame");
-    passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha");
-    passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count");
-    passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop");
-    // ... add more
-    if (passes) {
-        return ::testing::AssertionSuccess();
-    }
-    return message;
-}
-
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
-                                         const std::vector<RenderState>& val) {
-    ::testing::AssertionResult message = ::testing::AssertionFailure();
-    bool passed = true;
-    if (ref.size() != val.size()) {
-        message << "Expected " << ref.size() << " rects, got " << val.size() << ".";
-        passed = false;
-    }
-    for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) {
-        ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]);
-        if (rectResult == false) {
-            message << "First different rect at " << rectIndex << ": " << rectResult.message();
-            passed = false;
-            break;
-        }
-    }
-
-    if (passed) {
-        return ::testing::AssertionSuccess();
-    } else {
-        message << "\nReference:";
-        for (auto state = ref.begin(); state != ref.end(); ++state) {
-            message << "\n" << *state;
-        }
-        message << "\nActual:";
-        for (auto state = val.begin(); state != val.end(); ++state) {
-            message << "\n" << *state;
-        }
-    }
-    return message;
-}
-
-void startSurfaceFlinger() {
-    ALOGI("Start SurfaceFlinger");
-    system("start surfaceflinger");
-
-    sp<android::IServiceManager> sm(android::defaultServiceManager());
-    sp<android::IBinder> sf;
-    while (sf == nullptr) {
-        std::this_thread::sleep_for(10ms);
-        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
-    }
-    ALOGV("SurfaceFlinger running");
-}
-
-void stopSurfaceFlinger() {
-    ALOGI("Stop SurfaceFlinger");
-    system("stop surfaceflinger");
-    sp<android::IServiceManager> sm(android::defaultServiceManager());
-    sp<android::IBinder> sf;
-    while (sf != nullptr) {
-        std::this_thread::sleep_for(10ms);
-        sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName()));
-    }
-    ALOGV("SurfaceFlinger stopped");
-}
-
-////////////////////////////////////////////////
-
-void FakeHwcEnvironment::SetUp() {
-    ALOGI("Test env setup");
-    system("setenforce 0");
-    system("stop");
-    property_set("debug.sf.nobootanimation", "1");
-    {
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.sf.nobootanimation", value, "0");
-        LOG_FATAL_IF(atoi(value) != 1, "boot skip not set");
-    }
-    // TODO: Try registering the mock as the default service instead.
-    property_set("debug.sf.hwc_service_name", "mock");
-
-    // This allows tests/SF to register/load a HIDL service not listed in manifest files.
-    android::hardware::details::setTrebleTestingOverride(true);
-    property_set("debug.sf.treble_testing_override", "true");
-}
-
-void FakeHwcEnvironment::TearDown() {
-    ALOGI("Test env tear down");
-    system("stop");
-    // Wait for mock call signaling teardown?
-    property_set("debug.sf.nobootanimation", "0");
-    property_set("debug.sf.hwc_service_name", "default");
-    system("setenforce 1");
-    ALOGI("Test env tear down - done");
-}
-
-} // namespace sftest
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
deleted file mode 100644
index 383a111..0000000
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2017 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 "FakeComposerClient.h"
-
-#include <gui/SurfaceComposerClient.h>
-#include <log/log.h>
-#include <gtest/gtest.h>
-
-// clang-format off
-// Note: This needs to reside in the global namespace for the GTest to use it
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) {
-    return os << "(" << rect.left << ","
-              << rect.top << ","
-              << rect.right << ","
-              << rect.bottom << ")";
-}
-
-inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) {
-    return os << "(" << rect.left << ","
-              << rect.top << ","
-              << rect.right << ","
-              << rect.bottom << ")";
-}
-// clang-format on
-
-namespace sftest {
-
-class RenderState;
-
-// clang-format off
-inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) {
-    return a.top == b.top &&
-            a.left == b.left &&
-            a.bottom == b.bottom &&
-            a.right == b.right;
-}
-
-inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) {
-    return a.top == b.top &&
-            a.left == b.left &&
-            a.bottom == b.bottom &&
-            a.right == b.right;
-}
-// clang-format on
-
-inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) {
-    return !(a == b);
-}
-
-inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) {
-    return !(a == b);
-}
-
-::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val);
-::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref,
-                                         const std::vector<RenderState>& val);
-
-void startSurfaceFlinger();
-void stopSurfaceFlinger();
-
-class FakeHwcEnvironment : public ::testing::Environment {
-public:
-    virtual ~FakeHwcEnvironment() {}
-    void SetUp() override;
-    void TearDown() override;
-};
-
-/*
- * All surface state changes are supposed to happen inside a global
- * transaction. TransactionScope object at the beginning of
- * scope automates the process. The resulting scope gives a visual cue
- * on the span of the transaction as well.
- *
- * Closing the transaction is synchronous, i.e., it waits for
- * SurfaceFlinger to composite one frame. Now, the FakeComposerClient
- * is built to explicitly request vsyncs one at the time. A delayed
- * request must be made before closing the transaction or the test
- * thread stalls until SurfaceFlinger does an emergency vsync by
- * itself. TransactionScope encapsulates this vsync magic.
- */
-class TransactionScope : public android::SurfaceComposerClient::Transaction {
-public:
-    explicit TransactionScope(FakeComposerClient& composer) : Transaction(), mComposer(composer) {}
-
-    ~TransactionScope() {
-        int frameCount = mComposer.getFrameCount();
-        mComposer.runVSyncAfter(1ms);
-        LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
-        // Make sure that exactly one frame has been rendered.
-        mComposer.waitUntilFrame(frameCount + 1);
-        //        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
-        //                            "Unexpected frame advance. Delta: %d",
-        //                            mComposer.getFrameCount() - frameCount);
-    }
-
-    FakeComposerClient& mComposer;
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
deleted file mode 100644
index 5dc3778..0000000
--- a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
+++ /dev/null
@@ -1,47 +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 <composer-hal/2.4/ComposerClient.h>
-
-#include <gmock/gmock.h>
-
-using namespace android::hardware::graphics::common;
-using namespace android::hardware::graphics::composer;
-using namespace android::hardware::graphics::composer::V2_4;
-using namespace android::hardware::graphics::composer::V2_4::hal;
-using namespace android::hardware;
-using namespace std::chrono_literals;
-
-namespace sftest {
-
-// Mock class for ComposerHal. Implements only the functions used in the test.
-class MockComposerHal {
-public:
-    MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
-    MOCK_METHOD4(getDisplayAttribute_2_4,
-                 V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
-    MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
-    MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
-    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
-    MOCK_METHOD4(setActiveConfigWithConstraints,
-                 V2_4::Error(Display, Config,
-                             const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
-                             VsyncPeriodChangeTimeline*));
-};
-
-} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
deleted file mode 100644
index 40193f2..0000000
--- a/services/surfaceflinger/tests/fakehwc/RenderState.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017 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>
-
-namespace sftest {
-// Description of a rendered rectangle.  Should only contain
-// instructions necessary to rasterize the rectangle. The full scene
-// is given as a sorted list of rectangles, bottom layer at index 0.
-class RenderState {
-public:
-    RenderState() = default;
-    // Default copy-ctor
-
-    hwc_rect_t mDisplayFrame = {0, 0, 0, 0};
-    hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f};
-    std::vector<hwc_rect_t> mVisibleRegion;
-    hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE;
-    buffer_handle_t mBuffer = 0;
-    uint32_t mSwapCount = 0;   // How many set buffer calls to the layer.
-    int32_t mAcquireFence = 0; // Probably should not be here.
-    float mPlaneAlpha = 0.f;
-    hwc_color_t mLayerColor = {0, 0, 0, 0};
-    hwc_transform_t mTransform = static_cast<hwc_transform_t>(0);
-};
-
-} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
deleted file mode 100644
index b3b4ec1..0000000
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ /dev/null
@@ -1,1790 +0,0 @@
-/*
- * Copyright (C) 2017 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"
-#pragma clang diagnostic ignored "-Wextra"
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "FakeHwcTest"
-
-#include "FakeComposerClient.h"
-#include "FakeComposerService.h"
-#include "FakeComposerUtils.h"
-#include "MockComposerHal.h"
-
-#include <binder/Parcel.h>
-#include <gui/DisplayEventReceiver.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerDebugInfo.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-
-#include <android/hidl/manager/1.0/IServiceManager.h>
-#include <android/looper.h>
-#include <android/native_window.h>
-#include <binder/ProcessState.h>
-#include <hwbinder/ProcessState.h>
-#include <log/log.h>
-#include <private/gui/ComposerService.h>
-#include <ui/DisplayMode.h>
-#include <ui/DynamicDisplayInfo.h>
-#include <utils/Looper.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <limits>
-#include <thread>
-
-using namespace std::chrono_literals;
-
-using namespace android;
-using namespace android::hardware;
-
-using namespace sftest;
-
-namespace {
-
-// Mock test helpers
-using ::testing::_;
-using ::testing::DoAll;
-using ::testing::Return;
-using ::testing::SetArgPointee;
-
-using Transaction = SurfaceComposerClient::Transaction;
-using Attribute = V2_4::IComposerClient::Attribute;
-using Display = V2_1::Display;
-
-///////////////////////////////////////////////
-constexpr PhysicalDisplayId physicalIdFromHwcDisplayId(Display hwcId) {
-    return PhysicalDisplayId::fromPort(hwcId);
-}
-constexpr PhysicalDisplayId kPrimaryDisplayId = physicalIdFromHwcDisplayId(PRIMARY_DISPLAY);
-constexpr PhysicalDisplayId kExternalDisplayId = physicalIdFromHwcDisplayId(EXTERNAL_DISPLAY);
-
-struct TestColor {
-public:
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-};
-
-constexpr static TestColor RED = {195, 63, 63, 255};
-constexpr static TestColor LIGHT_RED = {255, 177, 177, 255};
-constexpr static TestColor GREEN = {63, 195, 63, 255};
-constexpr static TestColor BLUE = {63, 63, 195, 255};
-constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255};
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color,
-                             bool unlock = true) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = color.r;
-            pixel[1] = color.g;
-            pixel[2] = color.b;
-            pixel[3] = color.a;
-        }
-    }
-    if (unlock) {
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-    }
-}
-
-inline RenderState makeSimpleRect(int left, int top, int right, int bottom) {
-    RenderState res;
-    res.mDisplayFrame = hwc_rect_t{left, top, right, bottom};
-    res.mPlaneAlpha = 1.0f;
-    res.mSwapCount = 0;
-    res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left),
-                                  static_cast<float>(bottom - top)};
-    return res;
-}
-
-inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right,
-                                  unsigned int bottom) {
-    EXPECT_LE(left, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(top, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(right, static_cast<unsigned int>(INT_MAX));
-    EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX));
-    return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right),
-                          static_cast<int>(bottom));
-}
-
-///////////////////////////////////////////////
-template <typename FakeComposerService>
-class DisplayTest : public ::testing::Test {
-protected:
-    struct TestConfig {
-        int32_t id;
-        int32_t w;
-        int32_t h;
-        int32_t vsyncPeriod;
-        int32_t group;
-    };
-
-    static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
-        auto self = static_cast<DisplayTest*>(data);
-
-        ssize_t n;
-        DisplayEventReceiver::Event buffer[1];
-
-        while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
-            for (int i = 0; i < n; i++) {
-                self->mReceivedDisplayEvents.push_back(buffer[i]);
-            }
-        }
-        ALOGD_IF(n < 0, "Error reading events (%s)", strerror(-n));
-        return 1;
-    }
-
-    Error getDisplayAttributeNoMock(Display display, Config config,
-                                    V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
-        mFakeComposerClient->setMockHal(nullptr);
-        auto ret =
-                mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
-        mFakeComposerClient->setMockHal(mMockComposer.get());
-        return ret;
-    }
-
-    void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
-                                   Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
-        std::vector<Config> configIds;
-        for (size_t i = 0; i < testConfigs.size(); i++) {
-            configIds.push_back(testConfigs[i].id);
-
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
-                                                _))
-                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
-                                          Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
-                                                _))
-                    .WillRepeatedly(
-                            DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
-                    .WillRepeatedly(Return(Error::UNSUPPORTED));
-            EXPECT_CALL(*mMockComposer,
-                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
-                    .WillRepeatedly(Return(Error::UNSUPPORTED));
-        }
-
-        EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
-                                      Return(V2_1::Error::NONE)));
-
-        EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
-
-        EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
-                .WillRepeatedly(
-                        DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
-    }
-
-    void SetUp() override {
-        mMockComposer = std::make_unique<MockComposerHal>();
-        mFakeComposerClient = new FakeComposerClient();
-        mFakeComposerClient->setMockHal(mMockComposer.get());
-
-        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(mFakeComposerClient);
-        mFakeService = new FakeComposerService(client);
-        ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
-
-        android::hardware::ProcessState::self()->startThreadPool();
-        android::ProcessState::self()->startThreadPool();
-
-        setExpectationsForConfigs(PRIMARY_DISPLAY,
-                                  {{
-                                          .id = 1,
-                                          .w = 1920,
-                                          .h = 1024,
-                                          .vsyncPeriod = 16'666'666,
-                                          .group = 0,
-                                  }},
-                                  1, 16'666'666);
-
-        startSurfaceFlinger();
-
-        // Fake composer wants to enable VSync injection
-        mFakeComposerClient->onSurfaceFlingerStart();
-
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
-                                                 ISurfaceComposer::EventRegistration::modeChanged));
-        mLooper = new Looper(false);
-        mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
-    }
-
-    void TearDown() override {
-        mLooper = nullptr;
-        mReceiver = nullptr;
-
-        mComposerClient->dispose();
-        mComposerClient = nullptr;
-
-        // Fake composer needs to release SurfaceComposerClient before the stop.
-        mFakeComposerClient->onSurfaceFlingerStop();
-        stopSurfaceFlinger();
-
-        mFakeComposerClient->setMockHal(nullptr);
-
-        mFakeService = nullptr;
-        // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
-        // management.
-        mMockComposer = nullptr;
-    }
-
-    void waitForDisplayTransaction(Display display) {
-        // Both a refresh and a vsync event are needed to apply pending display
-        // transactions.
-        mFakeComposerClient->refreshDisplay(display);
-        mFakeComposerClient->runVSyncAndWait();
-
-        // Extra vsync and wait to avoid a 10% flake due to a race.
-        mFakeComposerClient->runVSyncAndWait();
-    }
-
-    bool waitForHotplugEvent(Display displayId, bool connected) {
-        return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
-    }
-
-    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-        int waitCount = 20;
-        while (waitCount--) {
-            while (!mReceivedDisplayEvents.empty()) {
-                auto event = mReceivedDisplayEvents.front();
-                mReceivedDisplayEvents.pop_front();
-
-                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-                         "event hotplug: displayId %s, connected %d",
-                         to_string(event.header.displayId).c_str(), event.hotplug.connected);
-
-                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
-                    event.header.displayId == displayId && event.hotplug.connected == connected) {
-                    return true;
-                }
-            }
-
-            mLooper->pollOnce(1);
-        }
-        return false;
-    }
-
-    bool waitForModeChangedEvent(Display display, int32_t modeId) {
-        PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
-        int waitCount = 20;
-        while (waitCount--) {
-            while (!mReceivedDisplayEvents.empty()) {
-                auto event = mReceivedDisplayEvents.front();
-                mReceivedDisplayEvents.pop_front();
-
-                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
-                         "event mode: displayId %s, modeId %d",
-                         to_string(event.header.displayId).c_str(), event.modeChange.modeId);
-
-                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE &&
-                    event.header.displayId == displayId && event.modeChange.modeId == modeId) {
-                    return true;
-                }
-            }
-
-            mLooper->pollOnce(1);
-        }
-        return false;
-    }
-
-    void Test_HotplugOneConfig() {
-        ALOGD("DisplayTest::Test_Hotplug_oneConfig");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 200,
-                                    .h = 400,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-            EXPECT_FALSE(display == nullptr);
-
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            const ui::Size& resolution = mode.resolution;
-            EXPECT_EQ(ui::Size(200, 400), resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-            EXPECT_TRUE(display == nullptr);
-
-            ui::DisplayMode mode;
-            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        }
-    }
-
-    void Test_HotplugTwoSeparateConfigs() {
-        ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 200,
-                                    .h = 400,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0},
-                                   {.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(200, 400), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 2);
-
-        // change active mode
-
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 800) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugTwoConfigsSameGroup() {
-        ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 31},
-                                   {.id = 3,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 31}},
-                                  2, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 2);
-
-        // change active mode
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugThreeConfigsMixedGroups() {
-        ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
-
-        setExpectationsForConfigs(EXTERNAL_DISPLAY,
-                                  {{.id = 2,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0},
-                                   {.id = 3,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 0},
-                                   {.id = 4,
-                                    .w = 1600,
-                                    .h = 3200,
-                                    .vsyncPeriod = 8'333'333,
-                                    .group = 1},
-                                   {.id = 5,
-                                    .w = 1600,
-                                    .h = 3200,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  2, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId);
-        EXPECT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 4);
-
-        // change active mode to 800x1600@90Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (size_t i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 800 && mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate,
-                                                                            modes[i].refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        // change active mode to 1600x3200@120Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.refreshRate == 1e9f / 8'333'333) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
-        EXPECT_EQ(1e9f / 8'333'333, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        // change active mode to 1600x3200@90Hz
-        if (mIs2_4Client) {
-            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
-                    .WillOnce(Return(V2_4::Error::NONE));
-        } else {
-            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
-                    .WillOnce(Return(V2_1::Error::NONE));
-        }
-
-        for (int i = 0; i < modes.size(); i++) {
-            const auto& mode = modes[i];
-            if (mode.resolution.getWidth() == 1600 && mode.refreshRate == 1e9f / 11'111'111) {
-                EXPECT_EQ(NO_ERROR,
-                          SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate,
-                                                                            mode.refreshRate));
-                waitForDisplayTransaction(EXTERNAL_DISPLAY);
-                EXPECT_TRUE(waitForModeChangedEvent(EXTERNAL_DISPLAY, i));
-                break;
-            }
-        }
-
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-        EXPECT_EQ(ui::Size(1600, 3200), mode.resolution);
-        EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-        mFakeComposerClient->clearFrames();
-        {
-            const ui::Size& resolution = mode.resolution;
-            auto surfaceControl =
-                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
-                                                   resolution.getWidth(), resolution.getHeight(),
-                                                   PIXEL_FORMAT_RGBA_8888, 0);
-            EXPECT_TRUE(surfaceControl != nullptr);
-            EXPECT_TRUE(surfaceControl->isValid());
-            fillSurfaceRGBA8(surfaceControl, BLUE);
-
-            {
-                TransactionScope ts(*mFakeComposerClient);
-                ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
-            }
-        }
-
-        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-        waitForDisplayTransaction(EXTERNAL_DISPLAY);
-        mFakeComposerClient->clearFrames();
-        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    }
-
-    void Test_HotplugPrimaryDisplay() {
-        ALOGD("DisplayTest::HotplugPrimaryDisplay");
-
-        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
-
-        waitForDisplayTransaction(PRIMARY_DISPLAY);
-
-        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-            EXPECT_TRUE(display == nullptr);
-
-            ui::DisplayMode mode;
-            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
-            EXPECT_NE(NO_ERROR, result);
-        }
-
-        mFakeComposerClient->clearFrames();
-
-        setExpectationsForConfigs(PRIMARY_DISPLAY,
-                                  {{.id = 1,
-                                    .w = 400,
-                                    .h = 200,
-                                    .vsyncPeriod = 16'666'666,
-                                    .group = 0}},
-                                  1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-
-        waitForDisplayTransaction(PRIMARY_DISPLAY);
-
-        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
-
-        {
-            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-            EXPECT_FALSE(display == nullptr);
-
-            ui::DisplayMode mode;
-            auto result = SurfaceComposerClient::getActiveDisplayMode(display, &mode);
-            EXPECT_EQ(NO_ERROR, result);
-            ASSERT_EQ(ui::Size(400, 200), mode.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-        }
-    }
-
-    void Test_SubsequentHotplugConnectUpdatesDisplay(Display hwcDisplayId) {
-        ALOGD("DisplayTest::Test_SubsequentHotplugConnectUpdatesDisplay");
-
-        // Send a hotplug connected event to set up the initial display modes.
-        // The primary display is already connected so this will update it.
-        // If we're running the test of an external display this will create it.
-        setExpectationsForConfigs(hwcDisplayId,
-                                  {{.id = 1,
-                                    .w = 800,
-                                    .h = 1600,
-                                    .vsyncPeriod = 11'111'111,
-                                    .group = 1}},
-                                  /* activeConfig */ 1, 11'111'111);
-
-        mFakeComposerClient->hotplugDisplay(hwcDisplayId,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(hwcDisplayId);
-        EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
-        const auto displayId = physicalIdFromHwcDisplayId(hwcDisplayId);
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(displayId);
-        EXPECT_FALSE(display == nullptr);
-
-        // Verify that the active mode and the supported moded are updated
-        {
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate);
-
-            ui::DynamicDisplayInfo info;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-            const auto& modes = info.supportedDisplayModes;
-            EXPECT_EQ(modes.size(), 1);
-        }
-
-        // Send another hotplug connected event
-        setExpectationsForConfigs(hwcDisplayId,
-                                  {
-                                          {.id = 1,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 16'666'666,
-                                           .group = 1},
-                                          {.id = 2,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 11'111'111,
-                                           .group = 1},
-                                          {.id = 3,
-                                           .w = 800,
-                                           .h = 1600,
-                                           .vsyncPeriod = 8'333'333,
-                                           .group = 1},
-                                  },
-                                  /* activeConfig */ 1, 16'666'666);
-
-        mFakeComposerClient->hotplugDisplay(hwcDisplayId,
-                                            V2_1::IComposerCallback::Connection::CONNECTED);
-        waitForDisplayTransaction(hwcDisplayId);
-        EXPECT_TRUE(waitForHotplugEvent(hwcDisplayId, true));
-
-        // Verify that the active mode and the supported moded are updated
-        {
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate);
-        }
-
-        ui::DynamicDisplayInfo info;
-        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
-        const auto& modes = info.supportedDisplayModes;
-        EXPECT_EQ(modes.size(), 3);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution);
-        EXPECT_EQ(1e9f / 16'666'666, modes[0].refreshRate);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[1].resolution);
-        EXPECT_EQ(1e9f / 11'111'111, modes[1].refreshRate);
-
-        EXPECT_EQ(ui::Size(800, 1600), modes[2].resolution);
-        EXPECT_EQ(1e9f / 8'333'333, modes[2].refreshRate);
-
-        // Verify that we are able to switch to any of the modes
-        for (int i = modes.size() - 1; i >= 0; i--) {
-            const auto hwcId = i + 1;
-            // Set up HWC expectations for the mode change
-            if (mIs2_4Client) {
-                EXPECT_CALL(*mMockComposer,
-                            setActiveConfigWithConstraints(hwcDisplayId, hwcId, _, _))
-                        .WillOnce(Return(V2_4::Error::NONE));
-            } else {
-                EXPECT_CALL(*mMockComposer, setActiveConfig(hwcDisplayId, hwcId))
-                        .WillOnce(Return(V2_1::Error::NONE));
-            }
-
-            EXPECT_EQ(NO_ERROR,
-                      SurfaceComposerClient::setDesiredDisplayModeSpecs(display, i, false,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate,
-                                                                        modes[i].refreshRate));
-            // We need to refresh twice - once to apply the pending mode change request,
-            // and once to process the change.
-            waitForDisplayTransaction(hwcDisplayId);
-            waitForDisplayTransaction(hwcDisplayId);
-            EXPECT_TRUE(waitForModeChangedEvent(hwcDisplayId, i))
-                    << "Failure while switching to mode " << i;
-
-            ui::DisplayMode mode;
-            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-            EXPECT_EQ(ui::Size(800, 1600), mode.resolution);
-            EXPECT_EQ(modes[i].refreshRate, mode.refreshRate);
-        }
-    }
-
-    sp<V2_1::IComposer> mFakeService;
-    sp<SurfaceComposerClient> mComposerClient;
-
-    std::unique_ptr<MockComposerHal> mMockComposer;
-    FakeComposerClient* mFakeComposerClient;
-
-    std::unique_ptr<DisplayEventReceiver> mReceiver;
-    sp<Looper> mLooper;
-    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-
-    static constexpr bool mIs2_4Client =
-            std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
-};
-
-using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
-
-// Tests that VSYNC injection can be safely toggled while invalidating.
-TEST_F(DisplayTest_2_1, VsyncInjection) {
-    const auto flinger = ComposerService::getComposerService();
-    bool enable = true;
-
-    for (int i = 0; i < 100; i++) {
-        flinger->enableVSyncInjections(enable);
-        enable = !enable;
-
-        constexpr uint32_t kForceInvalidate = 1004;
-        android::Parcel data, reply;
-        data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
-        EXPECT_EQ(NO_ERROR,
-                  android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
-
-        std::this_thread::sleep_for(5ms);
-    }
-}
-
-TEST_F(DisplayTest_2_1, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_1, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
-
-TEST_F(DisplayTest_2_2, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_2, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
-
-TEST_F(DisplayTest_2_3, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_3, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
-
-TEST_F(DisplayTest_2_4, HotplugOneConfig) {
-    Test_HotplugOneConfig();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
-    Test_HotplugTwoSeparateConfigs();
-}
-
-TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
-    Test_HotplugTwoConfigsSameGroup();
-}
-
-TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
-    Test_HotplugThreeConfigsMixedGroups();
-}
-
-TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
-    Test_HotplugPrimaryDisplay();
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesPrimaryDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(PRIMARY_DISPLAY);
-}
-
-TEST_F(DisplayTest_2_4, SubsequentHotplugConnectUpdatesExternalDisplay) {
-    Test_SubsequentHotplugConnectUpdatesDisplay(EXTERNAL_DISPLAY);
-}
-
-////////////////////////////////////////////////
-
-template <typename FakeComposerService>
-class TransactionTest : public ::testing::Test {
-protected:
-    // Layer array indexing constants.
-    constexpr static int BG_LAYER = 0;
-    constexpr static int FG_LAYER = 1;
-
-    static void SetUpTestCase() {
-        // TODO: See TODO comment at DisplayTest::SetUp for background on
-        // the lifetime of the FakeComposerClient.
-        sFakeComposer = new FakeComposerClient;
-        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(sFakeComposer);
-        sp<V2_1::IComposer> fakeService = new FakeComposerService(client);
-        (void)fakeService->registerAsService("mock");
-
-        android::hardware::ProcessState::self()->startThreadPool();
-        android::ProcessState::self()->startThreadPool();
-
-        startSurfaceFlinger();
-
-        // Fake composer wants to enable VSync injection
-        sFakeComposer->onSurfaceFlingerStart();
-    }
-
-    static void TearDownTestCase() {
-        // Fake composer needs to release SurfaceComposerClient before the stop.
-        sFakeComposer->onSurfaceFlingerStop();
-        stopSurfaceFlinger();
-        // TODO: This is deleted when the ComposerClient calls
-        // removeClient. Devise better lifetime control.
-        sFakeComposer = nullptr;
-    }
-
-    void SetUp() override {
-        ALOGI("TransactionTest::SetUp");
-        mComposerClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-        ALOGI("TransactionTest::SetUp - display");
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId);
-        ASSERT_FALSE(display == nullptr);
-
-        ui::DisplayMode mode;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
-        const ui::Size& resolution = mode.resolution;
-        mDisplayWidth = resolution.getWidth();
-        mDisplayHeight = resolution.getHeight();
-
-        // Background surface
-        mBGSurfaceControl =
-                mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
-                                               mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mBGSurfaceControl != nullptr);
-        ASSERT_TRUE(mBGSurfaceControl->isValid());
-        fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
-        // Foreground surface
-        mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
-                                                           PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(mFGSurfaceControl != nullptr);
-        ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
-        Transaction t;
-        t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
-
-        t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
-        t.show(mBGSurfaceControl);
-
-        t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-        t.show(mFGSurfaceControl);
-
-        // Synchronous transaction will stop this thread, so we set up a
-        // delayed, off-thread vsync request before closing the
-        // transaction. In the test code this is usually done with
-        // TransactionScope. Leaving here in the 'vanilla' form for
-        // reference.
-        ASSERT_EQ(0, sFakeComposer->getFrameCount());
-        sFakeComposer->runVSyncAfter(1ms);
-        t.apply();
-        sFakeComposer->waitUntilFrame(1);
-
-        // Reference data. This is what the HWC should see.
-        static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
-        mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
-        mBaseFrame[BG_LAYER].mSwapCount = 1;
-        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-        mBaseFrame[FG_LAYER].mSwapCount = 1;
-
-        auto frame = sFakeComposer->getFrameRects(0);
-        ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
-    }
-
-    void TearDown() override {
-        ALOGD("TransactionTest::TearDown");
-
-        mComposerClient->dispose();
-        mBGSurfaceControl = 0;
-        mFGSurfaceControl = 0;
-        mComposerClient = 0;
-
-        sFakeComposer->runVSyncAndWait();
-        mBaseFrame.clear();
-        sFakeComposer->clearFrames();
-        ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        std::vector<LayerDebugInfo> layers;
-        status_t result = sf->getLayerDebugInfo(&layers);
-        if (result != NO_ERROR) {
-            ALOGE("Failed to get layers %s %d", strerror(-result), result);
-        } else {
-            // If this fails, the test being torn down leaked layers.
-            EXPECT_EQ(0u, layers.size());
-            if (layers.size() > 0) {
-                for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
-                    std::cout << to_string(*layer).c_str();
-                }
-                // To ensure the next test has clean slate, will run the class
-                // tear down and setup here.
-                TearDownTestCase();
-                SetUpTestCase();
-            }
-        }
-        ALOGD("TransactionTest::TearDown - complete");
-    }
-
-    void Test_LayerMove() {
-        ALOGD("TransactionTest::LayerMove");
-
-        // The scope opens and closes a global transaction and, at the
-        // same time, makes sure the SurfaceFlinger progresses one frame
-        // after the transaction closes. The results of the transaction
-        // should be available in the latest frame stored by the fake
-        // composer.
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setPosition(mFGSurfaceControl, 128, 128);
-            // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
-            // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
-            //
-            // sFakeComposer->runVSyncAndWait();
-        }
-
-        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-        sFakeComposer->runVSyncAndWait();
-
-        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
-                                                      // there's no extra frames.
-
-        // NOTE: Frame 0 is produced in the SetUp.
-        auto frame1Ref = mBaseFrame;
-        frame1Ref[FG_LAYER].mDisplayFrame =
-                hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
-        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-        auto frame2Ref = frame1Ref;
-        frame2Ref[FG_LAYER].mSwapCount++;
-        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
-    }
-
-    void Test_LayerCrop() {
-        // TODO: Add scaling to confirm that crop happens in buffer space?
-        {
-            TransactionScope ts(*sFakeComposer);
-            Rect cropRect(16, 16, 32, 32);
-            ts.setCrop(mFGSurfaceControl, cropRect);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
-        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetLayer() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        // The layers will switch order, but both are rendered because the background layer is
-        // transparent (RGBA8888).
-        std::vector<RenderState> referenceFrame(2);
-        referenceFrame[0] = mBaseFrame[FG_LAYER];
-        referenceFrame[1] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetLayerOpaque() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-            ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
-                        layer_state_t::eLayerOpaque);
-        }
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-        // The former foreground layer is now covered with opaque layer - it should have disappeared
-        std::vector<RenderState> referenceFrame(1);
-        referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_SetLayerStack() {
-        ALOGD("TransactionTest::SetLayerStack");
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerShowHide() {
-        ALOGD("TransactionTest::LayerShowHide");
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.hide(mFGSurfaceControl);
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.show(mFGSurfaceControl);
-        }
-
-        // Foreground layer should be back
-        ASSERT_EQ(3, sFakeComposer->getFrameCount());
-        EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetAlpha() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setAlpha(mFGSurfaceControl, 0.75f);
-        }
-
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetFlags() {
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
-                        layer_state_t::eLayerHidden);
-        }
-
-        // Foreground layer should have disappeared.
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        std::vector<RenderState> refFrame(1);
-        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerSetMatrix() {
-        struct matrixTestData {
-            float matrix[4];
-            hwc_transform_t expectedTransform;
-            hwc_rect_t expectedDisplayFrame;
-        };
-
-        // The matrix operates on the display frame and is applied before
-        // the position is added. So, the foreground layer rect is (0, 0,
-        // 64, 64) is first transformed, potentially yielding negative
-        // coordinates and then the position (64, 64) is added yielding
-        // the final on-screen rectangles given.
-
-        const matrixTestData MATRIX_TESTS[7] = // clang-format off
-                {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
-                 {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
-                 {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
-                 {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
-                 {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
-                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
-                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
-        // clang-format on
-        constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
-        for (int i = 0; i < TEST_COUNT; i++) {
-            // TODO: How to leverage the HWC2 stringifiers?
-            const matrixTestData& xform = MATRIX_TESTS[i];
-            SCOPED_TRACE(i);
-            {
-                TransactionScope ts(*sFakeComposer);
-                ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
-                             xform.matrix[3]);
-            }
-
-            auto referenceFrame = mBaseFrame;
-            referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
-            referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
-            EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-        }
-    }
-
-    void Test_SetRelativeLayer() {
-        constexpr int RELATIVE_LAYER = 2;
-        auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
-                                                                     64, PIXEL_FORMAT_RGBA_8888, 0);
-        fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
-        // Now we stack the surface above the foreground surface and make sure it is visible.
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setPosition(relativeSurfaceControl, 64, 64);
-            ts.show(relativeSurfaceControl);
-            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1);
-        }
-        auto referenceFrame = mBaseFrame;
-        // NOTE: All three layers will be visible as the surfaces are
-        // transparent because of the RGBA format.
-        referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-        referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-        // A call to setLayer will override a call to setRelativeLayer
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setLayer(relativeSurfaceControl, 0);
-        }
-
-        // Previous top layer will now appear at the bottom.
-        auto referenceFrame2 = mBaseFrame;
-        referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
-        EXPECT_EQ(3, sFakeComposer->getFrameCount());
-        EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-    }
-
-    sp<SurfaceComposerClient> mComposerClient;
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-    std::vector<RenderState> mBaseFrame;
-    uint32_t mDisplayWidth;
-    uint32_t mDisplayHeight;
-
-    static inline FakeComposerClient* sFakeComposer;
-};
-
-using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
-    Test_LayerMove();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
-    Test_LayerCrop();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
-    Test_LayerSetLayer();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
-    Test_LayerSetLayerOpaque();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
-    Test_SetLayerStack();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
-    Test_LayerShowHide();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
-    Test_LayerSetAlpha();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
-    Test_LayerSetFlags();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
-    Test_LayerSetMatrix();
-}
-
-TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
-    Test_SetRelativeLayer();
-}
-
-template <typename FakeComposerService>
-class ChildLayerTest : public TransactionTest<FakeComposerService> {
-    using Base = TransactionTest<FakeComposerService>;
-
-protected:
-    constexpr static int CHILD_LAYER = 2;
-
-    void SetUp() override {
-        Base::SetUp();
-        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                                      PIXEL_FORMAT_RGBA_8888, 0,
-                                                      Base::mFGSurfaceControl->getHandle());
-        fillSurfaceRGBA8(mChild, LIGHT_GRAY);
-
-        Base::sFakeComposer->runVSyncAndWait();
-        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
-        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void TearDown() override {
-        mChild = 0;
-        Base::TearDown();
-    }
-
-    void Test_Positioning() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 10, 10);
-            // Move to the same position as in the original setup.
-            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame =
-                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-        }
-
-        auto referenceFrame2 = Base::mBaseFrame;
-        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
-        referenceFrame2[CHILD_LAYER].mDisplayFrame =
-                hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Cropping() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setCrop(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
-        }
-        // NOTE: The foreground surface would be occluded by the child
-        // now, but is included in the stack because the child is
-        // transparent.
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Constraints() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setPosition(mChild, 63, 63);
-        }
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
-        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_Scaling() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-        }
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
-        }
-
-        auto referenceFrame2 = Base::mBaseFrame;
-        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(mChild);
-            ts.setPosition(mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
-        }
-
-        auto referenceFrame2 = referenceFrame;
-        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
-        referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    sp<SurfaceControl> mChild;
-};
-
-using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
-    Test_Positioning();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) {
-    Test_Cropping();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
-    Test_Constraints();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
-    Test_Scaling();
-}
-
-TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
-    Test_LayerAlpha();
-}
-
-template <typename FakeComposerService>
-class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
-    using Base = ChildLayerTest<FakeComposerService>;
-
-protected:
-    void SetUp() override {
-        Base::SetUp();
-        Base::mChild =
-                Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
-                                                     PIXEL_FORMAT_RGBA_8888,
-                                                     ISurfaceComposerClient::eFXSurfaceEffect,
-                                                     Base::mFGSurfaceControl->getHandle());
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setColor(Base::mChild,
-                        {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
-            ts.setCrop(Base::mChild, Rect(0, 0, 10, 10));
-        }
-
-        Base::sFakeComposer->runVSyncAndWait();
-        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
-        Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
-        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(Base::mChild);
-            ts.setPosition(Base::mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(Base::mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
-        }
-
-        auto referenceFrame2 = referenceFrame;
-        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
-        referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
-        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
-    }
-
-    void Test_LayerZeroAlpha() {
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.show(Base::mChild);
-            ts.setPosition(Base::mChild, 0, 0);
-            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
-            ts.setAlpha(Base::mChild, 0.5);
-        }
-
-        auto referenceFrame = Base::mBaseFrame;
-        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
-        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
-
-        {
-            TransactionScope ts(*Base::sFakeComposer);
-            ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
-        }
-
-        std::vector<RenderState> refFrame(1);
-        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
-
-        EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
-    }
-};
-
-using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) {
-    Test_LayerAlpha();
-}
-
-TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
-    Test_LayerZeroAlpha();
-}
-} // namespace
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
-    ::testing::AddGlobalTestEnvironment(fakeEnvironment);
-    ::testing::InitGoogleMock(&argc, argv);
-    return RUN_ALL_TESTS();
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp
index aa6c74e..21ebaea 100644
--- a/services/surfaceflinger/tests/tracing/Android.bp
+++ b/services/surfaceflinger/tests/tracing/Android.bp
@@ -30,7 +30,7 @@
     ],
     test_suites: ["device-tests"],
     sanitize: {
-        address: false,
+        address: true,
     },
     srcs: [
         ":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index ac4354c..2b29530 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -20,7 +20,9 @@
 #include <fstream>
 #include <iostream>
 #include <string>
+#include <unordered_map>
 
+#include <LayerProtoHelper.h>
 #include <LayerTraceGenerator.h>
 #include <Tracing/TransactionProtoParser.h>
 #include <layerproto/LayerProtoHeader.h>
@@ -83,6 +85,98 @@
 
 std::vector<std::filesystem::path> TransactionTraceTestSuite::sTransactionTraces{};
 
+struct LayerInfo {
+    int32_t id;
+    std::string name;
+    int32_t parent;
+    int z;
+    uint64_t curr_frame;
+    float x;
+    float y;
+    uint32_t bufferWidth;
+    uint32_t bufferHeight;
+    Rect touchableRegionBounds;
+};
+
+bool operator==(const LayerInfo& lh, const LayerInfo& rh) {
+    return std::make_tuple(lh.id, lh.name, lh.parent, lh.z, lh.curr_frame, lh.bufferWidth,
+                           lh.bufferHeight, lh.touchableRegionBounds) ==
+            std::make_tuple(rh.id, rh.name, rh.parent, rh.z, rh.curr_frame, rh.bufferWidth,
+                            rh.bufferHeight, rh.touchableRegionBounds);
+}
+
+bool compareById(const LayerInfo& a, const LayerInfo& b) {
+    return a.id < b.id;
+}
+
+inline void PrintTo(const LayerInfo& info, ::std::ostream* os) {
+    *os << "Layer [" << info.id << "] name=" << info.name << " parent=" << info.parent
+        << " z=" << info.z << " curr_frame=" << info.curr_frame << " x=" << info.x
+        << " y=" << info.y << " bufferWidth=" << info.bufferWidth
+        << " bufferHeight=" << info.bufferHeight << "touchableRegionBounds={"
+        << info.touchableRegionBounds.left << "," << info.touchableRegionBounds.top << ","
+        << info.touchableRegionBounds.right << "," << info.touchableRegionBounds.bottom << "}";
+}
+
+struct find_id : std::unary_function<LayerInfo, bool> {
+    int id;
+    find_id(int id) : id(id) {}
+    bool operator()(LayerInfo const& m) const { return m.id == id; }
+};
+
+static LayerInfo getLayerInfoFromProto(::android::surfaceflinger::LayerProto& proto) {
+    Rect touchableRegionBounds = Rect::INVALID_RECT;
+    // ignore touchable region for layers without buffers, the new fe aggressively avoids
+    // calculating state for layers that are not visible which could lead to mismatches
+    if (proto.has_input_window_info() && proto.input_window_info().has_touchable_region() &&
+        proto.has_active_buffer()) {
+        Region touchableRegion;
+        LayerProtoHelper::readFromProto(proto.input_window_info().touchable_region(),
+                                        touchableRegion);
+        touchableRegionBounds = touchableRegion.bounds();
+    }
+
+    return {proto.id(),
+            proto.name(),
+            proto.parent(),
+            proto.z(),
+            proto.curr_frame(),
+            proto.has_position() ? proto.position().x() : -1,
+            proto.has_position() ? proto.position().y() : -1,
+            proto.has_active_buffer() ? proto.active_buffer().width() : 0,
+            proto.has_active_buffer() ? proto.active_buffer().height() : 0,
+            touchableRegionBounds};
+}
+
+static std::vector<LayerInfo> getLayerInfosFromProto(
+        android::surfaceflinger::LayersTraceProto& entry) {
+    std::unordered_map<int32_t /* snapshotId*/, int32_t /*layerId*/> snapshotIdToLayerId;
+    std::vector<LayerInfo> layers;
+    layers.reserve(static_cast<size_t>(entry.layers().layers_size()));
+    bool mapSnapshotIdToLayerId = false;
+    for (int i = 0; i < entry.layers().layers_size(); i++) {
+        auto layer = entry.layers().layers(i);
+        LayerInfo layerInfo = getLayerInfoFromProto(layer);
+
+        snapshotIdToLayerId[layerInfo.id] = static_cast<int32_t>(layer.original_id());
+        if (layer.original_id() != 0) {
+            mapSnapshotIdToLayerId = true;
+        }
+        layers.push_back(layerInfo);
+    }
+    std::sort(layers.begin(), layers.end(), compareById);
+
+    if (!mapSnapshotIdToLayerId) {
+        return layers;
+    }
+    for (auto& layer : layers) {
+        layer.id = snapshotIdToLayerId[layer.id];
+        auto it = snapshotIdToLayerId.find(layer.parent);
+        layer.parent = it == snapshotIdToLayerId.end() ? -1 : it->second;
+    }
+    return layers;
+}
+
 TEST_P(TransactionTraceTestSuite, validateEndState) {
     ASSERT_GT(mActualLayersTraceProto.entry_size(), 0);
     ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0);
@@ -92,19 +186,46 @@
     auto actualLastEntry = mActualLayersTraceProto.entry(mActualLayersTraceProto.entry_size() - 1);
 
     EXPECT_EQ(expectedLastEntry.layers().layers_size(), actualLastEntry.layers().layers_size());
-    for (int i = 0;
-         i < expectedLastEntry.layers().layers_size() && i < actualLastEntry.layers().layers_size();
-         i++) {
-        auto expectedLayer = expectedLastEntry.layers().layers(i);
-        auto actualLayer = actualLastEntry.layers().layers(i);
-        EXPECT_EQ(expectedLayer.id(), actualLayer.id());
-        EXPECT_EQ(expectedLayer.name(), actualLayer.name());
-        EXPECT_EQ(expectedLayer.parent(), actualLayer.parent());
-        EXPECT_EQ(expectedLayer.z(), actualLayer.z());
-        EXPECT_EQ(expectedLayer.curr_frame(), actualLayer.curr_frame());
-        ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayer.name().c_str(),
-              expectedLayer.id(), expectedLayer.parent(), expectedLayer.z(),
-              expectedLayer.curr_frame());
+
+    std::vector<LayerInfo> expectedLayers = getLayerInfosFromProto(expectedLastEntry);
+    std::vector<LayerInfo> actualLayers = getLayerInfosFromProto(actualLastEntry);
+    ;
+
+    size_t i = 0;
+    for (; i < actualLayers.size() && i < expectedLayers.size(); i++) {
+        auto it = std::find_if(actualLayers.begin(), actualLayers.end(),
+                               find_id(expectedLayers[i].id));
+        EXPECT_NE(it, actualLayers.end());
+        EXPECT_EQ(expectedLayers[i], *it);
+        ALOGV("Validating %s[%d] parent=%d z=%d frame=%" PRIu64, expectedLayers[i].name.c_str(),
+              expectedLayers[i].id, expectedLayers[i].parent, expectedLayers[i].z,
+              expectedLayers[i].curr_frame);
+    }
+
+    EXPECT_EQ(expectedLayers.size(), actualLayers.size());
+
+    if (i < actualLayers.size()) {
+        for (size_t j = 0; j < actualLayers.size(); j++) {
+            if (std::find_if(expectedLayers.begin(), expectedLayers.end(),
+                             find_id(actualLayers[j].id)) == expectedLayers.end()) {
+                ALOGD("actualLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, actualLayers[j].id,
+                      actualLayers[j].name.c_str(), actualLayers[j].parent, actualLayers[j].z,
+                      actualLayers[j].curr_frame);
+            }
+        }
+        FAIL();
+    }
+
+    if (i < expectedLayers.size()) {
+        for (size_t j = 0; j < expectedLayers.size(); j++) {
+            if (std::find_if(actualLayers.begin(), actualLayers.end(),
+                             find_id(expectedLayers[j].id)) == actualLayers.end()) {
+                ALOGD("expectedLayers [%d]:%s parent=%d z=%d frame=%" PRIu64, expectedLayers[j].id,
+                      expectedLayers[j].name.c_str(), expectedLayers[j].parent, expectedLayers[j].z,
+                      expectedLayers[j].curr_frame);
+            }
+        }
+        FAIL();
     }
 }
 
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_b275630566.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b275630566.winscope
new file mode 100644
index 0000000..fe504d7
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_b275630566.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
index 9e4005c..296d2fd 100644
--- a/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
new file mode 100644
index 0000000..ae54415
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/layers_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b275630566.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b275630566.winscope
new file mode 100644
index 0000000..6f7ba15
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_b275630566.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
index 8356ae7..8d03df4 100644
--- a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_boot.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
new file mode 100644
index 0000000..022861c
--- /dev/null
+++ b/services/surfaceflinger/tests/tracing/testdata/transactions_trace_nodisplayfound.winscope
Binary files differ
diff --git a/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
new file mode 100644
index 0000000..7077523
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/ActiveDisplayRotationFlagsTest.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct ActiveDisplayRotationFlagsTest : DisplayTransactionTest {
+    static constexpr bool kWithMockScheduler = false;
+    ActiveDisplayRotationFlagsTest() : DisplayTransactionTest(kWithMockScheduler) {}
+
+    void SetUp() override {
+        injectMockScheduler(kInnerDisplayId);
+
+        // Inject inner and outer displays with uninitialized power modes.
+        constexpr bool kInitPowerMode = false;
+        {
+            InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+            mInnerDisplay = injector.inject();
+        }
+        {
+            OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+            auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+            injector.setPowerMode(std::nullopt);
+            mOuterDisplay = injector.inject();
+        }
+
+        mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+        mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+        // The flags are a static variable, so by modifying them in the test, we
+        // are modifying the real ones used by SurfaceFlinger. Save the original
+        // flags so we can restore them on teardown. This isn't perfect - the
+        // phone may have been rotated during the test, so we're restoring the
+        // wrong flags. But if the phone is rotated, this may also fail the test.
+        mOldRotationFlags = mFlinger.mutableActiveDisplayRotationFlags();
+
+        // Reset to the expected default state.
+        mFlinger.mutableActiveDisplayRotationFlags() = ui::Transform::ROT_0;
+    }
+
+    void TearDown() override { mFlinger.mutableActiveDisplayRotationFlags() = mOldRotationFlags; }
+
+    static inline PhysicalDisplayId kInnerDisplayId = InnerDisplayVariant::DISPLAY_ID::get();
+    static inline PhysicalDisplayId kOuterDisplayId = OuterDisplayVariant::DISPLAY_ID::get();
+
+    sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
+    ui::Transform::RotationFlags mOldRotationFlags;
+};
+
+TEST_F(ActiveDisplayRotationFlagsTest, defaultRotation) {
+    ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_90;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_90, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotate90_inactive) {
+    auto displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_90;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_0, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_innerActive) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, rotateBoth_outerActive) {
+    mFlinger.mutableActiveDisplayId() = kOuterDisplayId;
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+TEST_F(ActiveDisplayRotationFlagsTest, onActiveDisplayChanged) {
+    auto displayToken = mInnerDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_180;
+
+    displayToken = mOuterDisplay->getDisplayToken().promote();
+    mFlinger.mutableDrawingState().displays.editValueFor(displayToken).orientation = ui::ROTATION_0;
+    mFlinger.mutableCurrentState().displays.editValueFor(displayToken).orientation =
+            ui::ROTATION_270;
+
+    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    ASSERT_EQ(ui::Transform::ROT_180, SurfaceFlinger::getActiveDisplayRotationFlags());
+
+    mFlinger.onActiveDisplayChanged(mInnerDisplay.get(), *mOuterDisplay);
+    ASSERT_EQ(ui::Transform::ROT_270, SurfaceFlinger::getActiveDisplayRotationFlags());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
deleted file mode 100644
index 53de4a6..0000000
--- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp
+++ /dev/null
@@ -1,283 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "AidlPowerHalWrapperTest"
-
-#include <android-base/stringprintf.h>
-#include <android/hardware/power/IPower.h>
-#include <android/hardware/power/IPowerHintSession.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <algorithm>
-#include <chrono>
-#include <memory>
-#include "DisplayHardware/PowerAdvisor.h"
-#include "android/hardware/power/WorkDuration.h"
-#include "binder/Status.h"
-#include "log/log_main.h"
-#include "mock/DisplayHardware/MockIPower.h"
-#include "mock/DisplayHardware/MockIPowerHintSession.h"
-#include "utils/Timers.h"
-
-using namespace android;
-using namespace android::Hwc2::mock;
-using namespace android::hardware::power;
-using namespace std::chrono_literals;
-using namespace testing;
-
-namespace android::Hwc2::impl {
-
-class AidlPowerHalWrapperTest : public testing::Test {
-public:
-    void SetUp() override;
-
-protected:
-    std::unique_ptr<AidlPowerHalWrapper> mWrapper = nullptr;
-    sp<NiceMock<MockIPower>> mMockHal = nullptr;
-    sp<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
-    void verifyAndClearExpectations();
-    void sendActualWorkDurationGroup(std::vector<WorkDuration> durations,
-                                     std::chrono::nanoseconds sleepBeforeLastSend);
-    std::chrono::nanoseconds mAllowedDeviation;
-    std::chrono::nanoseconds mStaleTimeout;
-};
-
-void AidlPowerHalWrapperTest::SetUp() {
-    mMockHal = new NiceMock<MockIPower>();
-    mMockSession = new NiceMock<MockIPowerHintSession>();
-    ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok()));
-    mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal);
-    mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count());
-    mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation};
-    mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout;
-}
-
-void AidlPowerHalWrapperTest::verifyAndClearExpectations() {
-    Mock::VerifyAndClearExpectations(mMockHal.get());
-    Mock::VerifyAndClearExpectations(mMockSession.get());
-}
-
-void AidlPowerHalWrapperTest::sendActualWorkDurationGroup(
-        std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend) {
-    for (size_t i = 0; i < durations.size(); i++) {
-        if (i == durations.size() - 1) {
-            std::this_thread::sleep_for(sleepBeforeLastSend);
-        }
-        auto duration = durations[i];
-        mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos);
-    }
-}
-
-WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) {
-    WorkDuration duration;
-    duration.durationNanos = durationNanos.count();
-    duration.timeStampNanos = timeStampNanos;
-    return duration;
-}
-
-WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) {
-    return toWorkDuration(timePair.first, timePair.second);
-}
-
-std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) {
-    std::ostringstream os;
-    for (auto duration : durations) {
-        os << duration.toString();
-        os << "\n";
-    }
-    return os.str();
-}
-
-namespace {
-TEST_F(AidlPowerHalWrapperTest, supportsPowerHintSession) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-    Mock::VerifyAndClearExpectations(mMockHal.get());
-    ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_))
-            .WillByDefault(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
-    auto newWrapper = AidlPowerHalWrapper(mMockHal);
-    EXPECT_FALSE(newWrapper.supportsPowerHintSession());
-}
-
-TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    EXPECT_TRUE(mWrapper->startPowerHintSession());
-    EXPECT_FALSE(mWrapper->startPowerHintSession());
-}
-
-TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    threadIds = {2, 3};
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    EXPECT_CALL(*mMockSession.get(), close()).Times(1);
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_EQ(mWrapper->getPowerHintSessionThreadIds(), threadIds);
-    verifyAndClearExpectations();
-
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)).Times(0);
-    EXPECT_CALL(*mMockSession.get(), close()).Times(0);
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    verifyAndClearExpectations();
-}
-
-TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    std::chrono::nanoseconds base = 100ms;
-    // test cases with target work duration and whether it should update hint against baseline 100ms
-    const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases =
-            {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}};
-
-    for (const auto& test : testCases) {
-        // reset to 100ms baseline
-        mWrapper->setTargetWorkDuration(1);
-        mWrapper->setTargetWorkDuration(base.count());
-
-        auto target = test.first;
-        EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(target.count()))
-                .Times(test.second ? 1 : 0);
-        mWrapper->setTargetWorkDuration(target.count());
-        verifyAndClearExpectations();
-    }
-}
-
-TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration_shouldReconnectOnError) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
-    mWrapper->setTargetWorkDuration(1);
-    EXPECT_TRUE(mWrapper->shouldReconnectHAL());
-}
-
-TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    auto base = toWorkDuration(100ms, 0);
-    // test cases with actual work durations and whether it should update hint against baseline
-    // 100ms
-    const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>>
-            testCases = {{{{-1ms, 100}}, false},
-                         {{{100ms - (mAllowedDeviation / 2), 100}}, false},
-                         {{{100ms + (mAllowedDeviation / 2), 100}}, false},
-                         {{{100ms + (mAllowedDeviation + 1ms), 100}}, true},
-                         {{{100ms - (mAllowedDeviation + 1ms), 100}}, true},
-                         {{{100ms, 100}, {200ms, 200}}, true},
-                         {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}};
-
-    for (const auto& test : testCases) {
-        // reset actual duration
-        sendActualWorkDurationGroup({base}, mStaleTimeout);
-
-        auto raw = test.first;
-        std::vector<WorkDuration> durations(raw.size());
-        std::transform(raw.begin(), raw.end(), durations.begin(),
-                       [](auto d) { return toWorkDuration(d); });
-        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
-                .Times(test.second ? 1 : 0);
-        sendActualWorkDurationGroup(durations, 0ms);
-        verifyAndClearExpectations();
-    }
-}
-
-TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-
-    auto base = toWorkDuration(100ms, 0);
-    // test cases with actual work durations and whether it should update hint against baseline
-    // 100ms
-    const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>,
-                                 std::chrono::nanoseconds, bool>>
-            testCases = {{{{100ms, 100}}, mStaleTimeout, true},
-                         {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true},
-                         {{{100ms, 100}}, mStaleTimeout / 2, false}};
-
-    for (const auto& test : testCases) {
-        // reset actual duration
-        sendActualWorkDurationGroup({base}, mStaleTimeout);
-
-        auto raw = std::get<0>(test);
-        std::vector<WorkDuration> durations(raw.size());
-        std::transform(raw.begin(), raw.end(), durations.begin(),
-                       [](auto d) { return toWorkDuration(d); });
-        EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations))
-                .Times(std::get<2>(test) ? 1 : 0);
-        sendActualWorkDurationGroup(durations, std::get<1>(test));
-        verifyAndClearExpectations();
-    }
-}
-
-TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_shouldReconnectOnError) {
-    ASSERT_TRUE(mWrapper->supportsPowerHintSession());
-
-    std::vector<int32_t> threadIds = {1, 2};
-    mWrapper->setPowerHintSessionThreadIds(threadIds);
-    EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _))
-            .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok())));
-    ASSERT_TRUE(mWrapper->startPowerHintSession());
-    verifyAndClearExpectations();
-    WorkDuration duration;
-    duration.durationNanos = 1;
-    EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_))
-            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE)));
-    sendActualWorkDurationGroup({duration}, 0ms);
-    EXPECT_TRUE(mWrapper->shouldReconnectHAL());
-}
-
-} // namespace
-} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 339d746..db81bad 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -24,7 +24,7 @@
 filegroup {
     name: "libsurfaceflinger_mock_sources",
     srcs: [
-        "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
+        "mock/DisplayHardware/MockPowerHalController.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockHWC2.cpp",
         "mock/DisplayHardware/MockIPower.cpp",
@@ -34,9 +34,9 @@
         "mock/MockFrameTimeline.cpp",
         "mock/MockFrameTracer.cpp",
         "mock/MockNativeWindowSurface.cpp",
-        "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
         "mock/MockVsyncController.cpp",
+        "mock/MockVSyncDispatch.cpp",
         "mock/MockVSyncTracker.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
@@ -70,10 +70,9 @@
         ":libsurfaceflinger_mock_sources",
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
-        "AidlPowerHalWrapperTest.cpp",
-        "CachingTest.cpp",
+        "ActiveDisplayRotationFlagsTest.cpp",
+        "BackgroundExecutorTest.cpp",
         "CompositionTest.cpp",
-        "DispSyncSourceTest.cpp",
         "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
         "DisplayDevice_GetBestColorModeTest.cpp",
@@ -86,6 +85,7 @@
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
         "FrameRateOverrideMappingsTest.cpp",
+        "FrameRateSelectionPriorityTest.cpp",
         "FrameTimelineTest.cpp",
         "GameModeTest.cpp",
         "HWComposerTest.cpp",
@@ -93,6 +93,9 @@
         "LayerHistoryTest.cpp",
         "LayerInfoTest.cpp",
         "LayerMetadataTest.cpp",
+        "LayerHierarchyTest.cpp",
+        "LayerLifecycleManagerTest.cpp",
+        "LayerSnapshotTest.cpp",
         "LayerTest.cpp",
         "LayerTestUtils.cpp",
         "MessageQueueTest.cpp",
@@ -101,18 +104,22 @@
         "SurfaceFlinger_DestroyDisplayTest.cpp",
         "SurfaceFlinger_DisplayModeSwitching.cpp",
         "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
+        "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
         "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+        "SurfaceFlinger_GetDisplayStatsTest.cpp",
+        "SurfaceFlinger_HdrOutputControlTest.cpp",
         "SurfaceFlinger_HotplugTest.cpp",
+        "SurfaceFlinger_InitializeDisplaysTest.cpp",
+        "SurfaceFlinger_MultiDisplayPacesetterTest.cpp",
         "SurfaceFlinger_NotifyPowerBoostTest.cpp",
-        "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
         "SurfaceFlinger_PowerHintTest.cpp",
         "SurfaceFlinger_SetDisplayStateTest.cpp",
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
+        "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
         "SchedulerTest.cpp",
         "SetFrameRateTest.cpp",
-        "RefreshRateConfigsTest.cpp",
-        "RefreshRateSelectionTest.cpp",
+        "RefreshRateSelectorTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
@@ -124,31 +131,37 @@
         "TransactionTracingTest.cpp",
         "TunnelModeEnabledReporterTest.cpp",
         "StrongTypingTest.cpp",
+        "VSyncCallbackRegistrationTest.cpp",
         "VSyncDispatchTimerQueueTest.cpp",
         "VSyncDispatchRealtimeTest.cpp",
         "VsyncModulatorTest.cpp",
         "VSyncPredictorTest.cpp",
         "VSyncReactorTest.cpp",
         "VsyncConfigurationTest.cpp",
+        "VsyncScheduleTest.cpp",
+        "WindowInfosListenerInvokerTest.cpp",
     ],
 }
 
 cc_defaults {
     name: "libsurfaceflinger_mocks_defaults",
+    defaults: [
+        "android.hardware.graphics.common-ndk_static",
+        "android.hardware.graphics.composer3-ndk_static",
+        "librenderengine_deps",
+    ],
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
-        "android.hardware.graphics.common-V4-ndk",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
         "android.hardware.graphics.composer@2.4",
-        "android.hardware.graphics.composer3-V1-ndk",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power@1.2",
         "android.hardware.power@1.3",
-        "android.hardware.power-V2-cpp",
+        "android.hardware.power-V4-cpp",
         "libaidlcommonsupport",
         "libcompositionengine_mocks",
         "libcompositionengine",
@@ -165,7 +178,6 @@
         "libtimestats_atoms_proto",
         "libtimestats_proto",
         "libtonemap",
-        "libtrace_proto",
         "perfetto_trace_protos",
     ],
     shared_libs: [
@@ -188,6 +200,7 @@
         "libinput",
         "liblog",
         "libnativewindow",
+        "libpowermanager",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libSurfaceFlingerProp",
diff --git a/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
new file mode 100644
index 0000000..5413bae
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/BackgroundExecutorTest.cpp
@@ -0,0 +1,57 @@
+#include <gtest/gtest.h>
+#include <condition_variable>
+
+#include "BackgroundExecutor.h"
+
+namespace android {
+
+class BackgroundExecutorTest : public testing::Test {};
+
+namespace {
+
+TEST_F(BackgroundExecutorTest, singleProducer) {
+    std::mutex mutex;
+    std::condition_variable condition_variable;
+    bool backgroundTaskComplete = false;
+
+    BackgroundExecutor::getInstance().sendCallbacks(
+            {[&mutex, &condition_variable, &backgroundTaskComplete]() {
+                std::lock_guard<std::mutex> lock{mutex};
+                condition_variable.notify_one();
+                backgroundTaskComplete = true;
+            }});
+
+    std::unique_lock<std::mutex> lock{mutex};
+    condition_variable.wait(lock, [&backgroundTaskComplete]() { return backgroundTaskComplete; });
+    ASSERT_TRUE(backgroundTaskComplete);
+}
+
+TEST_F(BackgroundExecutorTest, multipleProducers) {
+    std::mutex mutex;
+    std::condition_variable condition_variable;
+    const int backgroundTaskCount = 10;
+    int backgroundTaskCompleteCount = 0;
+
+    for (int i = 0; i < backgroundTaskCount; i++) {
+        std::thread([&mutex, &condition_variable, &backgroundTaskCompleteCount]() {
+            BackgroundExecutor::getInstance().sendCallbacks(
+                    {[&mutex, &condition_variable, &backgroundTaskCompleteCount]() {
+                        std::lock_guard<std::mutex> lock{mutex};
+                        backgroundTaskCompleteCount++;
+                        if (backgroundTaskCompleteCount == backgroundTaskCount) {
+                            condition_variable.notify_one();
+                        }
+                    }});
+        }).detach();
+    }
+
+    std::unique_lock<std::mutex> lock{mutex};
+    condition_variable.wait(lock, [&backgroundTaskCompleteCount]() {
+        return backgroundTaskCompleteCount == backgroundTaskCount;
+    });
+    ASSERT_EQ(backgroundTaskCount, backgroundTaskCompleteCount);
+}
+
+} // namespace
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
deleted file mode 100644
index 6f85498..0000000
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ /dev/null
@@ -1,94 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CachingTest"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/BufferQueue.h>
-#include "BufferStateLayer.h"
-
-namespace android {
-
-class SlotGenerationTest : public testing::Test {
-protected:
-    sp<BufferStateLayer::HwcSlotGenerator> mHwcSlotGenerator =
-            sp<BufferStateLayer::HwcSlotGenerator>::make();
-    sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-    sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-    sp<GraphicBuffer> mBuffer3{new GraphicBuffer(10, 10, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
-};
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
-    sp<IBinder> binder = new BBinder();
-    // test getting invalid client_cache_id
-    client_cache_t id;
-    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
-    EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
-    sp<IBinder> binder = new BBinder();
-    client_cache_t id;
-    id.token = binder;
-    id.id = 0;
-    int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
-    EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-
-    client_cache_t idB;
-    idB.token = binder;
-    idB.id = 1;
-    slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
-    EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
-    slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
-    EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
-    slot = mHwcSlotGenerator->getHwcCacheSlot(id);
-    EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
-    sp<IBinder> binder = new BBinder();
-    std::vector<client_cache_t> ids;
-    uint32_t cacheId = 0;
-    // fill up cache
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        client_cache_t id;
-        id.token = binder;
-        id.id = cacheId;
-        ids.push_back(id);
-
-        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
-        EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
-        cacheId++;
-    }
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
-        EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
-    }
-
-    for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        client_cache_t id;
-        id.token = binder;
-        id.id = cacheId;
-        int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
-        EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
-        cacheId++;
-    }
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index fbc532e..e8a9cfe 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -37,10 +37,7 @@
 #include <system/window.h>
 #include <utils/String8.h>
 
-#include "BufferQueueLayer.h"
-#include "ContainerLayer.h"
 #include "DisplayRenderArea.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
@@ -102,7 +99,7 @@
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-        setupScheduler();
+        mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
 
         EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
@@ -125,34 +122,6 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    void setupScheduler() {
-        auto eventThread = std::make_unique<mock::EventThread>();
-        auto sfEventThread = std::make_unique<mock::EventThread>();
-
-        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        auto vsyncController = std::make_unique<mock::VsyncController>();
-        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*vsyncTracker, currentPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-
-        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                std::move(eventThread), std::move(sfEventThread),
-                                TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                                TestableSurfaceFlinger::kTwoDisplayModes);
-    }
-
     void setupForceGeometryDirty() {
         // TODO: This requires the visible region and other related
         // state to be set, and is problematic for BufferLayers since they are
@@ -177,13 +146,14 @@
     bool mDisplayOff = false;
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
-    sp<DisplayDevice> mExternalDisplay;
     sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
-            new compositionengine::mock::DisplaySurface();
-    mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+            sp<compositionengine::mock::DisplaySurface>::make();
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
     std::vector<sp<Layer>> mAuxiliaryLayers;
 
-    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    sp<GraphicBuffer> mBuffer =
+            sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888,
+                                    GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN);
     ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
 
     Hwc2::mock::Composer* mComposer = nullptr;
@@ -230,13 +200,15 @@
     constexpr bool regionSampling = false;
 
     auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
-                                                ui::Dataspace::V0_SRGB, ui::Transform::ROT_0);
+                                                ui::Dataspace::V0_SRGB, true, true);
 
     auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
         return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
-                                                   CaptureArgs::UNSET_UID, visitor);
+                                                   CaptureArgs::UNSET_UID, {}, visitor);
     };
 
+    auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+
     const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
             GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     mCaptureScreenBuffer =
@@ -245,8 +217,8 @@
                                                                       HAL_PIXEL_FORMAT_RGBA_8888, 1,
                                                                       usage);
 
-    auto future = mFlinger.renderScreenImpl(*renderArea, traverseLayers, mCaptureScreenBuffer,
-                                            forSystem, regionSampling);
+    auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
+                                            mCaptureScreenBuffer, forSystem, regionSampling);
     ASSERT_TRUE(future.valid());
     const auto fenceResult = future.get();
 
@@ -309,16 +281,23 @@
                 compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
                                                        ceDisplayArgs);
 
-        test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
-                                                   ui::DisplayConnectionType::Internal, HWC_DISPLAY,
-                                                   true /* isPrimary */)
-                                 .setDisplaySurface(test->mDisplaySurface)
-                                 .setNativeWindow(test->mNativeWindow)
-                                 .setSecure(Derived::IS_SECURE)
-                                 .setPowerMode(Derived::INIT_POWER_MODE)
-                                 .inject();
-        Mock::VerifyAndClear(test->mNativeWindow);
-        test->mDisplay->setLayerStack(LAYER_STACK);
+        constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal;
+        constexpr bool kIsPrimary = true;
+
+        test->mDisplay =
+                FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                          kDisplayConnectionType, HWC_DISPLAY, kIsPrimary)
+                        .setDisplaySurface(test->mDisplaySurface)
+                        .setNativeWindow(test->mNativeWindow)
+                        .setSecure(Derived::IS_SECURE)
+                        .setPowerMode(Derived::INIT_POWER_MODE)
+                        .setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector())
+                        .skipRegisterDisplay()
+                        .inject();
+        Mock::VerifyAndClear(test->mNativeWindow.get());
+
+        constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal;
+        test->mDisplay->setLayerFilter({LAYER_STACK, kIsInternal});
     }
 
     template <typename Case>
@@ -351,15 +330,13 @@
                 .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
                                     const std::vector<renderengine::LayerSettings>&,
                                     const std::shared_ptr<renderengine::ExternalTexture>&,
-                                    const bool, base::unique_fd&&)
-                                        -> std::future<renderengine::RenderEngineResult> {
+                                    const bool, base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
-                    return futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()});
+                    return futureOf<FenceResult>(Fence::NO_FENCE);
                 });
     }
 
@@ -404,16 +381,14 @@
                 .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
                                     const std::vector<renderengine::LayerSettings>&,
                                     const std::shared_ptr<renderengine::ExternalTexture>&,
-                                    const bool, base::unique_fd&&)
-                                        -> std::future<renderengine::RenderEngineResult> {
+                                    const bool, base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.clip);
                     EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
-                    return futureOf<renderengine::RenderEngineResult>(
-                            {NO_ERROR, base::unique_fd()});
+                    return futureOf<FenceResult>(Fence::NO_FENCE);
                 });
     }
 
@@ -492,65 +467,31 @@
     static constexpr IComposerClient::BlendMode BLENDMODE =
             IComposerClient::BlendMode::PREMULTIPLIED;
 
-    static void enqueueBuffer(CompositionTest*, sp<BufferQueueLayer> layer) {
-        auto producer = layer->getProducer();
-
-        IGraphicBufferProducer::QueueBufferOutput qbo;
-        status_t result = producer->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &qbo);
-        if (result != NO_ERROR) {
-            ALOGE("Failed to connect() (%d)", result);
-            return;
-        }
-
-        int slot;
-        sp<Fence> fence;
-        result = producer->dequeueBuffer(&slot, &fence, LayerProperties::WIDTH,
-                                         LayerProperties::HEIGHT, LayerProperties::FORMAT,
-                                         LayerProperties::USAGE, nullptr, nullptr);
-        if (result != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
-            ALOGE("Failed to dequeueBuffer() (%d)", result);
-            return;
-        }
-
-        sp<GraphicBuffer> buffer;
-        result = producer->requestBuffer(slot, &buffer);
-        if (result != NO_ERROR) {
-            ALOGE("Failed to requestBuffer() (%d)", result);
-            return;
-        }
-
-        IGraphicBufferProducer::QueueBufferInput qbi(systemTime(), false /* isAutoTimestamp */,
-                                                     LayerProperties::DATASPACE,
-                                                     Rect(LayerProperties::WIDTH,
-                                                          LayerProperties::HEIGHT),
-                                                     LayerProperties::SCALING_MODE,
-                                                     LayerProperties::TRANSFORM, Fence::NO_FENCE);
-        result = producer->queueBuffer(slot, qbi, &qbo);
-        if (result != NO_ERROR) {
-            ALOGE("Failed to queueBuffer (%d)", result);
-            return;
-        }
-    }
-
-    static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
-        // TODO: Eliminate the complexity of actually creating a buffer
-        layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT);
-        status_t err =
-                layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
-                                                  LayerProperties::FORMAT);
-        ASSERT_EQ(NO_ERROR, err);
+    static void setupLatchedBuffer(CompositionTest* test, sp<Layer> layer) {
         Mock::VerifyAndClear(test->mRenderEngine);
 
-        EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
-        enqueueBuffer(test, layer);
-        Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
+        const auto buffer = std::make_shared<
+                renderengine::mock::FakeExternalTexture>(LayerProperties::WIDTH,
+                                                         LayerProperties::HEIGHT,
+                                                         DEFAULT_TEXTURE_ID,
+                                                         LayerProperties::FORMAT,
+                                                         LayerProperties::USAGE |
+                                                                 GraphicBuffer::USAGE_HW_TEXTURE);
+
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+        layerDrawingState.buffer = buffer;
+        layerDrawingState.acquireFence = Fence::NO_FENCE;
+        layerDrawingState.dataspace = ui::Dataspace::UNKNOWN;
+        layer->setSurfaceDamageRegion(
+                Region(Rect(LayerProperties::HEIGHT, LayerProperties::WIDTH)));
 
         bool ignoredRecomputeVisibleRegions;
-        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
+        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
-    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         setupLatchedBuffer(test, layer);
     }
 
@@ -640,7 +581,7 @@
                 .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
                               const std::vector<renderengine::LayerSettings>& layerSettings,
                               const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -648,9 +589,7 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so gtet the back layer.
-                    std::future<renderengine::RenderEngineResult> resultFuture =
-                            futureOf<renderengine::RenderEngineResult>(
-                                    {NO_ERROR, base::unique_fd()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupREBufferCompositionCommonCallExpectations "
@@ -666,7 +605,7 @@
                     EXPECT_EQ(false, layer.source.buffer.isOpaque);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(ui::Dataspace::V0_SRGB, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
                 });
@@ -693,7 +632,7 @@
                 .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
                               const std::vector<renderengine::LayerSettings>& layerSettings,
                               const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -701,9 +640,7 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
-                    std::future<renderengine::RenderEngineResult> resultFuture =
-                            futureOf<renderengine::RenderEngineResult>(
-                                    {NO_ERROR, base::unique_fd()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE()
                                 << "layerSettings was not expected to be empty in "
@@ -717,7 +654,7 @@
                               layer.source.solidColor);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(ui::Dataspace::V0_SRGB, layer.sourceDataspace);
                     EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
                     return resultFuture;
                 });
@@ -738,11 +675,14 @@
     using Base = BaseLayerProperties<SidebandLayerProperties>;
     static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         sp<NativeHandle> stream =
                 NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                      false);
         test->mFlinger.setLayerSidebandStream(layer, stream);
+        auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
+        layerDrawingState.crop =
+                Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -774,7 +714,7 @@
                 .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
                               const std::vector<renderengine::LayerSettings>& layerSettings,
                               const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
-                              base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+                              base::unique_fd&&) -> std::future<FenceResult> {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                     EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                               displaySettings.physicalDisplay);
@@ -782,9 +722,7 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
-                    std::future<renderengine::RenderEngineResult> resultFuture =
-                            futureOf<renderengine::RenderEngineResult>(
-                                    {NO_ERROR, base::unique_fd()});
+                    std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE);
                     if (layerSettings.empty()) {
                         ADD_FAILURE() << "layerSettings was not expected to be empty in "
                                          "setupInsecureREBufferCompositionCommonCallExpectations "
@@ -796,7 +734,7 @@
                     EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
                     EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.y);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+                    EXPECT_EQ(ui::Dataspace::V0_SRGB, layer.sourceDataspace);
                     EXPECT_EQ(1.0f, layer.alpha);
                     return resultFuture;
                 });
@@ -821,14 +759,14 @@
 struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> {
     using Base = BaseLayerProperties<CursorLayerProperties>;
 
-    static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) {
+    static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
         Base::setupLayerState(test, layer);
         test->mFlinger.setLayerPotentialCursor(layer, true);
     }
 };
 
 struct NoLayerVariant {
-    using FlingerLayerType = sp<BufferQueueLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); }
     static void injectLayer(CompositionTest*, FlingerLayerType) {}
@@ -862,8 +800,6 @@
     static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = LAYER_STACK;
-        layerDrawingState.width = 100;
-        layerDrawingState.height = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
         layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
@@ -881,6 +817,7 @@
         Mock::VerifyAndClear(test->mComposer);
 
         test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+        test->mFlinger.mutableVisibleRegionsDirty() = true;
     }
 
     static void cleanupInjectedLayers(CompositionTest* test) {
@@ -889,6 +826,7 @@
 
         test->mDisplay->getCompositionDisplay()->clearOutputLayers();
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+        test->mFlinger.mutablePreviouslyComposedLayers().clear();
 
         // Layer should be unregistered with scheduler.
         test->mFlinger.commit();
@@ -899,13 +837,13 @@
 template <typename LayerProperties>
 struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<EffectLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
-            return new EffectLayer(
-                    LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
-                                      LayerProperties::LAYER_FLAGS, LayerMetadata()));
+        FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+            return sp<Layer>::make(LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(),
+                                                     "test-layer", LayerProperties::LAYER_FLAGS,
+                                                     LayerMetadata()));
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
@@ -935,17 +873,17 @@
 template <typename LayerProperties>
 struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<BufferQueueLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
 
         FlingerLayerType layer =
-                Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
+                Base::template createLayerWithFactory<Layer>(test, [test]() {
                     LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
                                            LayerProperties::LAYER_FLAGS, LayerMetadata());
                     args.textureName = test->mFlinger.mutableTexturePool().back();
-                    return new BufferQueueLayer(args);
+                    return sp<Layer>::make(args);
                 });
 
         LayerProperties::setupLayerState(test, layer);
@@ -987,12 +925,12 @@
 template <typename LayerProperties>
 struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<ContainerLayer>;
+    using FlingerLayerType = sp<Layer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
         LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
                                LayerProperties::LAYER_FLAGS, LayerMetadata());
-        FlingerLayerType layer = new ContainerLayer(args);
+        FlingerLayerType layer = sp<Layer>::make(args);
         Base::template initLayerDrawingStateAndComputeBounds(test, layer);
         return layer;
     }
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
deleted file mode 100644
index 67ace1a..0000000
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ /dev/null
@@ -1,315 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <log/log.h>
-
-#include "AsyncCallRecorder.h"
-#include "Scheduler/DispSyncSource.h"
-#include "Scheduler/VSyncDispatch.h"
-#include "mock/MockVSyncTracker.h"
-
-namespace android {
-namespace {
-
-using namespace std::chrono_literals;
-using namespace testing;
-
-class MockVSyncDispatch : public scheduler::VSyncDispatch {
-public:
-    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
-    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-
-    MockVSyncDispatch() {
-        ON_CALL(*this, registerCallback)
-                .WillByDefault(
-                        [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
-                               std::string) {
-                            CallbackToken token(mNextToken);
-                            mNextToken++;
-
-                            mCallbacks.emplace(token, CallbackData(callback));
-                            ALOGD("registerCallback: %zu", token.value());
-                            return token;
-                        });
-
-        ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
-            ALOGD("unregisterCallback: %zu", token.value());
-            mCallbacks.erase(token);
-        });
-
-        ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
-            ALOGD("schedule: %zu", token.value());
-            if (mCallbacks.count(token) == 0) {
-                ALOGD("schedule: callback %zu not registered", token.value());
-                return scheduler::ScheduleResult{};
-            }
-
-            auto& callback = mCallbacks.at(token);
-            callback.scheduled = true;
-            callback.vsyncTime = timing.earliestVsync;
-            callback.targetWakeupTime =
-                    timing.earliestVsync - timing.workDuration - timing.readyDuration;
-            ALOGD("schedule: callback %zu scheduled", token.value());
-            return scheduler::ScheduleResult{callback.targetWakeupTime};
-        });
-
-        ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
-            ALOGD("cancel: %zu", token.value());
-            if (mCallbacks.count(token) == 0) {
-                ALOGD("cancel: callback %zu is not registered", token.value());
-                return scheduler::CancelResult::Error;
-            }
-
-            auto& callback = mCallbacks.at(token);
-            callback.scheduled = false;
-            ALOGD("cancel: callback %zu cancelled", token.value());
-            return scheduler::CancelResult::Cancelled;
-        });
-    }
-
-    void triggerCallbacks() {
-        ALOGD("triggerCallbacks");
-        for (auto& [token, callback] : mCallbacks) {
-            if (callback.scheduled) {
-                ALOGD("triggerCallbacks: callback %zu", token.value());
-                callback.scheduled = false;
-                callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
-            } else {
-                ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
-            }
-        }
-    }
-
-private:
-    struct CallbackData {
-        explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
-              : func(std::move(func)) {}
-
-        std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
-        bool scheduled = false;
-        nsecs_t vsyncTime = 0;
-        nsecs_t targetWakeupTime = 0;
-        nsecs_t readyTime = 0;
-    };
-
-    std::unordered_map<CallbackToken, CallbackData> mCallbacks;
-    size_t mNextToken;
-};
-
-class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
-protected:
-    DispSyncSourceTest();
-    ~DispSyncSourceTest() override;
-
-    void SetUp() override;
-    void createDispSyncSource();
-
-    void onVSyncEvent(nsecs_t when, VSyncSource::VSyncData) override;
-
-    std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
-    std::unique_ptr<mock::VSyncTracker> mVSyncTracker;
-    std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
-
-    AsyncCallRecorder<void (*)(nsecs_t, VSyncSource::VSyncData)> mVSyncEventCallRecorder;
-
-    static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
-    static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
-    static constexpr int mIterations = 100;
-    const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
-    const std::string mName = "DispSyncSourceTest";
-};
-
-DispSyncSourceTest::DispSyncSourceTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-DispSyncSourceTest::~DispSyncSourceTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-void DispSyncSourceTest::SetUp() {
-    mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
-    mVSyncTracker = std::make_unique<mock::VSyncTracker>();
-}
-
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when, VSyncSource::VSyncData vsyncData) {
-    ALOGD("onVSyncEvent: %" PRId64, when);
-
-    mVSyncEventCallRecorder.recordCall(when, vsyncData);
-}
-
-void DispSyncSourceTest::createDispSyncSource() {
-    mDispSyncSource = std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, *mVSyncTracker,
-                                                                  mWorkDuration, mReadyDuration,
-                                                                  true, mName.c_str());
-    mDispSyncSource->setCallback(this);
-}
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-
-TEST_F(DispSyncSourceTest, createDispSync) {
-    EXPECT_TRUE(mVSyncDispatch);
-}
-
-TEST_F(DispSyncSourceTest, createDispSyncSource) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
-            .WillOnce(Return(scheduler::CancelResult::Cancelled));
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-}
-
-TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    // DispSyncSource starts with Vsync disabled
-    mVSyncDispatch->triggerCallbacks();
-    EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-TEST_F(DispSyncSourceTest, waitForCallbacks) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(mIterations + 1);
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    mDispSyncSource->setVSyncEnabled(true);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when,
-                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
-                          mReadyDuration.count());
-    }
-}
-
-TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(1);
-
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    mDispSyncSource->setVSyncEnabled(true);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(mIterations);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when,
-                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
-                          mReadyDuration.count());
-    }
-
-    const auto newDuration = mWorkDuration / 2;
-    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
-                                              return timings.workDuration == newDuration.count() &&
-                                                      timings.readyDuration == 0;
-                                          })))
-            .Times(1);
-    mDispSyncSource->setDuration(newDuration, 0ns);
-
-    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
-                                              return timings.workDuration == newDuration.count() &&
-                                                      timings.readyDuration == 0;
-                                          })))
-            .Times(mIterations);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when, vsyncData.expectedPresentationTime - newDuration.count());
-    }
-
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-}
-
-TEST_F(DispSyncSourceTest, getLatestVsyncData) {
-    const nsecs_t now = systemTime();
-    const nsecs_t expectedPresentationTime =
-            now + mWorkDuration.count() + mReadyDuration.count() + 1;
-    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
-            .WillOnce(Return(expectedPresentationTime));
-    {
-        InSequence seq;
-        EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-        EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-        EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    }
-
-    createDispSyncSource();
-    EXPECT_TRUE(mDispSyncSource);
-
-    const auto vsyncData = mDispSyncSource->getLatestVSyncData();
-    ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
-    EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 93af225..60ad7a3 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include "DisplayTransactionTestHelpers.h"
+#include "mock/MockFrameRateMode.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -29,6 +30,7 @@
 
 class InitiateModeChangeTest : public DisplayTransactionTest {
 public:
+    using Action = DisplayDevice::DesiredActiveModeAction;
     using Event = scheduler::DisplayModeEvent;
 
     void SetUp() override {
@@ -42,6 +44,7 @@
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.configureAndCommit();
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
                            .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60)
@@ -55,31 +58,42 @@
     static constexpr DisplayModeId kModeId90{1};
     static constexpr DisplayModeId kModeId120{2};
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
 };
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
     EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    // Setting another mode should be cached but return false
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    // Setting another mode should be cached but return None
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 }
 
 TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
 
     mDisplay->clearDesiredActiveModeState();
@@ -87,9 +101,11 @@
 }
 
 TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -100,18 +116,27 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
     ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
+TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+    EXPECT_EQ(Action::InitiateRenderRateSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
+    EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
 TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
 NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -122,21 +147,23 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5ddd1c8..e32cf88 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -29,15 +29,12 @@
 
 using android::hardware::graphics::composer::hal::HWDisplayId;
 
-using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-
-DisplayTransactionTest::DisplayTransactionTest() {
+DisplayTransactionTest::DisplayTransactionTest(bool withMockScheduler) {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    // Default to no wide color display support configured
-    mFlinger.mutableHasWideColorDisplay() = false;
+    mFlinger.mutableSupportsWideColor() = false;
     mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
 
     mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
@@ -49,9 +46,11 @@
         return nullptr;
     });
 
-    injectMockScheduler();
+    if (withMockScheduler) {
+        injectMockScheduler(PhysicalDisplayId::fromPort(0));
+    }
+
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
-    mFlinger.mutableInterceptor() = mSurfaceInterceptor;
 
     injectMockComposer(0);
 }
@@ -63,21 +62,26 @@
     mFlinger.resetScheduler(nullptr);
 }
 
-void DisplayTransactionTest::injectMockScheduler() {
+void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) {
+    LOG_ALWAYS_FATAL_IF(mFlinger.scheduler());
+
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
-            .WillOnce(Return(
-                    new EventThreadConnection(mEventThread, /*callingUid=*/0, ResyncCallback())));
+            .WillOnce(Return(sp<EventThreadConnection>::make(mEventThread,
+                                                             mock::EventThread::kCallingUid,
+                                                             ResyncCallback())));
 
     EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
-            .WillOnce(Return(
-                    new EventThreadConnection(mSFEventThread, /*callingUid=*/0, ResyncCallback())));
+            .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread,
+                                                             mock::EventThread::kCallingUid,
+                                                             ResyncCallback())));
 
-    mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
-                            std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
+    mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
+                            std::make_shared<mock::VSyncTracker>(),
                             std::unique_ptr<EventThread>(mEventThread),
                             std::unique_ptr<EventThread>(mSFEventThread),
+                            TestableSurfaceFlinger::DefaultDisplayMode{displayId},
                             TestableSurfaceFlinger::SchedulerCallbackImpl::kMock);
 }
 
@@ -100,8 +104,8 @@
     // This setup is only expected once per test.
     ASSERT_TRUE(mConsumer == nullptr && mProducer == nullptr);
 
-    mConsumer = new mock::GraphicBufferConsumer();
-    mProducer = new mock::GraphicBufferProducer();
+    mConsumer = sp<mock::GraphicBufferConsumer>::make();
+    mProducer = sp<mock::GraphicBufferProducer>::make();
 
     mFlinger.setCreateBufferQueueFunction([this](auto outProducer, auto outConsumer, bool) {
         *outProducer = mProducer;
@@ -121,7 +125,12 @@
 }
 
 bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) const {
-    return mFlinger.hwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
+    const auto& map = mFlinger.hwcPhysicalDisplayIdMap();
+
+    const auto it = map.find(hwcDisplayId);
+    if (it == map.end()) return false;
+
+    return mFlinger.hwcDisplayData().count(it->second) == 1;
 }
 
 bool DisplayTransactionTest::hasTransactionFlagSet(int32_t flag) const {
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 60f773f..e64cb38 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -50,7 +50,6 @@
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSurfaceInterceptor.h"
 #include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
@@ -86,10 +85,11 @@
     // --------------------------------------------------------------------
     // Mock/Fake injection
 
-    void injectMockScheduler();
+    void injectMockScheduler(PhysicalDisplayId);
     void injectMockComposer(int virtualDisplayCount);
     void injectFakeBufferQueueFactory();
     void injectFakeNativeWindowSurfaceFactory();
+
     sp<DisplayDevice> injectDefaultInternalDisplay(
             std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)> injectExtra) {
         return mFakeDisplayInjector.injectInternalDisplay(injectExtra);
@@ -114,8 +114,10 @@
     // Test instances
 
     TestableSurfaceFlinger mFlinger;
-    sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
-    sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+    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;
 
     FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow};
@@ -125,10 +127,7 @@
     // to keep a reference to them for use in setting up call expectations.
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     Hwc2::mock::Composer* mComposer = nullptr;
-    sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
 
-    mock::VsyncController* mVsyncController = new mock::VsyncController;
-    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
     mock::EventThread* mEventThread = new mock::EventThread;
     mock::EventThread* mSFEventThread = new mock::EventThread;
 
@@ -138,7 +137,7 @@
     surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
 
 protected:
-    DisplayTransactionTest();
+    DisplayTransactionTest(bool withMockScheduler = true);
 };
 
 constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
@@ -157,7 +156,6 @@
 #define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
 
 BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
 BOOL_SUBSTITUTE(Primary);
 BOOL_SUBSTITUTE(Secure);
 BOOL_SUBSTITUTE(Virtual);
@@ -237,8 +235,8 @@
 //     1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
 //     2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
 //     3) GpuVirtualDisplayIdType for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
-          Secure secure, Primary primary, int grallocUsage, int displayFlags>
+template <typename DisplayIdType, int width, int height, Async async, Secure secure,
+          Primary primary, int grallocUsage, int displayFlags>
 struct DisplayVariant {
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
     using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
@@ -254,9 +252,6 @@
     static constexpr Virtual VIRTUAL =
             IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
 
-    // When creating native window surfaces for the framebuffer, whether those should be critical
-    static constexpr Critical CRITICAL = critical;
-
     // When creating native window surfaces for the framebuffer, whether those should be async
     static constexpr Async ASYNC = async;
 
@@ -360,6 +355,7 @@
     }
 
     // Called by tests to inject a HWC display setup
+    template <bool kInitPowerMode = true>
     static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
         const auto displayId = DisplayVariant::DISPLAY_ID::get();
         ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -368,18 +364,21 @@
                 .setHwcDisplayId(HWC_DISPLAY_ID)
                 .setResolution(DisplayVariant::RESOLUTION)
                 .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
-                .setPowerMode(INIT_POWER_MODE)
+                .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
                 .inject(&test->mFlinger, test->mComposer);
     }
 
     // Called by tests to inject a HWC display setup
+    template <bool kInitPowerMode = true>
     static void injectHwcDisplay(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
                 .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
                                 Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
-                .WillOnce(Return(Error::NONE));
-        injectHwcDisplayWithNoDefaultCapabilities(test);
+        if constexpr (kInitPowerMode) {
+            EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+                    .WillOnce(Return(Error::NONE));
+        }
+        injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
     }
 
     static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -439,14 +438,18 @@
         }
     }
 
+    template <bool kFailedHotplug = false>
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
-        constexpr auto CONNECTION_TYPE =
-                PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal
-                ? IComposerClient::DisplayConnectionType::INTERNAL
-                : IComposerClient::DisplayConnectionType::EXTERNAL;
+        if constexpr (!kFailedHotplug) {
+            constexpr auto CONNECTION_TYPE =
+                    PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal
+                    ? IComposerClient::DisplayConnectionType::INTERNAL
+                    : IComposerClient::DisplayConnectionType::EXTERNAL;
 
-        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+            EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+                    .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE),
+                                    Return(hal::V2_4::Error::NONE)));
+        }
 
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
                 .WillOnce(Return(hal::Error::NONE));
@@ -477,17 +480,16 @@
 
 constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
 
-template <typename PhysicalDisplay, int width, int height, Critical critical>
+template <typename PhysicalDisplay, int width, int height>
 struct PhysicalDisplayVariant
-      : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
-                       Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
-                       GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
-        HwcDisplayVariant<
-                PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
-                DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
-                               Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
-                               GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
-                PhysicalDisplay> {};
+      : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE,
+                       Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY,
+                       PHYSICAL_DISPLAY_FLAGS>,
+        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+                          DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+                                         Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+                                         GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+                          PhysicalDisplay> {};
 
 template <bool hasIdentificationData>
 struct PrimaryDisplay {
@@ -499,14 +501,16 @@
     static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
 };
 
-template <bool hasIdentificationData>
-struct ExternalDisplay {
-    static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External;
+template <ui::DisplayConnectionType connectionType, bool hasIdentificationData>
+struct SecondaryDisplay {
+    static constexpr auto CONNECTION_TYPE = connectionType;
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 254;
     static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
-    static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+    static constexpr auto GET_IDENTIFICATION_DATA =
+            connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid
+                                                                  : getExternalEdid;
 };
 
 struct TertiaryDisplay {
@@ -516,15 +520,18 @@
     static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
 };
 
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
-        PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+using PrimaryDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160>;
 
-// An external display is physical display that is not critical.
+using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>;
+using OuterDisplayVariant =
+        PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080,
+                               2092>;
+
 using ExternalDisplayVariant =
-        PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+        PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920,
+                               1280>;
 
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>;
 
 // A virtual display not supported by the HWC.
 constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -533,12 +540,11 @@
 
 template <int width, int height, Secure secure>
 struct NonHwcVirtualDisplayVariant
-      : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
-                       Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
-                       VIRTUAL_DISPLAY_FLAGS> {
-    using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE,
-                                Async::TRUE, secure, Primary::FALSE,
-                                GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>;
+      : DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure, Primary::FALSE,
+                       GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS> {
+    using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure,
+                                Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
+                                VIRTUAL_DISPLAY_FLAGS>;
 
     static void injectHwcDisplay(DisplayTransactionTest*) {}
 
@@ -580,17 +586,14 @@
 
 template <int width, int height, Secure secure>
 struct HwcVirtualDisplayVariant
-      : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
-                       secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
-                       VIRTUAL_DISPLAY_FLAGS>,
-        HwcDisplayVariant<
-                HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
-                DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
-                               Async::TRUE, secure, Primary::FALSE,
-                               GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> {
-    using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
-                                Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER,
-                                VIRTUAL_DISPLAY_FLAGS>;
+      : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure,
+                       Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>,
+        HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+                          DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE,
+                                         secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
+                                         VIRTUAL_DISPLAY_FLAGS>> {
+    using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure,
+                                Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER, VIRTUAL_DISPLAY_FLAGS>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
     static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -680,12 +683,10 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = true;
+        test->mFlinger.mutableSupportsWideColor() = true;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
     }
 };
@@ -697,12 +698,11 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = false;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = false;
+        test->mFlinger.mutableSupportsWideColor() = false;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
         EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
         EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
     }
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index c033af8..5fed9b4 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -24,18 +24,23 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
+#include <scheduler/VsyncConfig.h>
 #include <utils/Errors.h>
 
 #include "AsyncCallRecorder.h"
 #include "DisplayHardware/DisplayMode.h"
 #include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
 
 using namespace std::chrono_literals;
 using namespace std::placeholders;
 
 using testing::_;
 using testing::Invoke;
+using testing::Return;
 
 namespace android {
 
@@ -50,29 +55,18 @@
 
 constexpr std::chrono::duration VSYNC_PERIOD(16ms);
 
-class MockVSyncSource : public VSyncSource {
-public:
-    const char* getName() const override { return "test"; }
-
-    MOCK_METHOD1(setVSyncEnabled, void(bool));
-    MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
-    MOCK_METHOD2(setDuration,
-                 void(std::chrono::nanoseconds workDuration,
-                      std::chrono::nanoseconds readyDuration));
-    MOCK_METHOD1(pauseVsyncCallback, void(bool));
-    MOCK_METHOD(VSyncSource::VSyncData, getLatestVSyncData, (), (const, override));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
 } // namespace
 
 class EventThreadTest : public testing::Test {
 protected:
+    static constexpr std::chrono::nanoseconds kWorkDuration = 0ms;
+    static constexpr std::chrono::nanoseconds kReadyDuration = 3ms;
+
     class MockEventThreadConnection : public EventThreadConnection {
     public:
         MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
                                   ResyncCallback&& resyncCallback,
-                                  ISurfaceComposer::EventRegistrationFlags eventRegistration)
+                                  EventRegistrationFlags eventRegistration)
               : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
                                       eventRegistration) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
@@ -84,23 +78,22 @@
     EventThreadTest();
     ~EventThreadTest() override;
 
-    void createThread(std::unique_ptr<VSyncSource>);
-    sp<MockEventThreadConnection> createConnection(
-            ConnectionEventRecorder& recorder,
-            ISurfaceComposer::EventRegistrationFlags eventRegistration = {},
-            uid_t ownerUid = mConnectionUid);
+    void setupEventThread(std::chrono::nanoseconds vsyncPeriod);
+    sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
+                                                   EventRegistrationFlags eventRegistration = {},
+                                                   uid_t ownerUid = mConnectionUid);
 
-    void expectVSyncSetEnabledCallReceived(bool expectedState);
+    void expectVSyncCallbackScheduleReceived(bool expectState);
     void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
                                             std::chrono::nanoseconds expectedReadyDuration);
-    VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
-    void expectInterceptCallReceived(nsecs_t expectedTimestamp);
     void expectVsyncEventReceivedByConnection(const char* name,
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
-    void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
-                                               VSyncSource::VSyncData preferredVsyncData);
+    void expectVsyncEventFrameTimelinesCorrect(
+            nsecs_t expectedTimestamp, gui::VsyncEventData::FrameTimeline preferredVsyncData);
+    void expectVsyncEventDataFrameTimelinesValidLength(VsyncEventData vsyncEventData,
+                                                       std::chrono::nanoseconds vsyncPeriod);
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -110,18 +103,31 @@
     void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                             std::vector<FrameRateOverride>);
 
-    AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
-    AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
-    AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
-            mVSyncSetDurationCallRecorder;
+    void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
+                      nsecs_t deadlineTimestamp) {
+        mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp);
+    }
+
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
+                                          scheduler::VSyncDispatch::ScheduleTiming)>
+            mVSyncCallbackScheduleRecorder{0};
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
+                                          scheduler::VSyncDispatch::ScheduleTiming)>
+            mVSyncCallbackUpdateRecorder{0};
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback,
+                                                        std::string)>
+            mVSyncCallbackRegisterRecorder{scheduler::VSyncDispatch::CallbackToken(0)};
+    AsyncCallRecorder<void (*)(scheduler::VSyncDispatch::CallbackToken)>
+            mVSyncCallbackUnregisterRecorder;
     AsyncCallRecorder<void (*)()> mResyncCallRecorder;
-    AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
     ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
 
-    MockVSyncSource* mVSyncSource;
-    VSyncSource::Callback* mCallback = nullptr;
+    std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule;
     std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
@@ -136,29 +142,20 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    auto vsyncSource = std::make_unique<MockVSyncSource>();
-    mVSyncSource = vsyncSource.get();
-
-    EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
-            .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
-
-    EXPECT_CALL(*mVSyncSource, setCallback(_))
-            .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
-
-    EXPECT_CALL(*mVSyncSource, setDuration(_, _))
-            .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable()));
-
-    createThread(std::move(vsyncSource));
-    mConnection = createConnection(mConnectionEventCallRecorder,
-                                   ISurfaceComposer::EventRegistration::modeChanged |
-                                           ISurfaceComposer::EventRegistration::frameRateOverride);
-    mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
-                                            ISurfaceComposer::EventRegistration::modeChanged,
-                                            mThrottledConnectionUid);
-
-    // A display must be connected for VSYNC events to be delivered.
-    mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
-    expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
+    auto mockDispatchPtr = std::make_shared<mock::VSyncDispatch>();
+    mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>(
+            new scheduler::VsyncSchedule(INTERNAL_DISPLAY_ID,
+                                         std::make_shared<mock::VSyncTracker>(), mockDispatchPtr,
+                                         nullptr));
+    mock::VSyncDispatch& mockDispatch = *mockDispatchPtr;
+    EXPECT_CALL(mockDispatch, registerCallback(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackRegisterRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, schedule(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackScheduleRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, update(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackUpdateRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, unregisterCallback(_))
+            .WillRepeatedly(Invoke(mVSyncCallbackUnregisterRecorder.getInvocable()));
 }
 
 EventThreadTest::~EventThreadTest() {
@@ -166,62 +163,64 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 
+    mThread.reset();
     // EventThread should unregister itself as VSyncSource callback.
-    EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
+    EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
 }
 
-void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+void EventThreadTest::setupEventThread(std::chrono::nanoseconds vsyncPeriod) {
     const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
         return (uid == mThrottledConnectionUid);
     };
-    const auto getVsyncPeriod = [](uid_t uid) {
-        return VSYNC_PERIOD.count();
-    };
+    const auto getVsyncPeriod = [vsyncPeriod](uid_t uid) { return vsyncPeriod.count(); };
 
     mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
-    mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
-                                                  mInterceptVSyncCallRecorder.getInvocable(),
-                                                  throttleVsync, getVsyncPeriod);
+    mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule,
+                                                  mTokenManager.get(), throttleVsync,
+                                                  getVsyncPeriod, kWorkDuration, kReadyDuration);
 
     // EventThread should register itself as VSyncSource callback.
-    mCallback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(mCallback);
+    EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
+
+    mConnection =
+            createConnection(mConnectionEventCallRecorder,
+                             gui::ISurfaceComposer::EventRegistration::modeChanged |
+                                     gui::ISurfaceComposer::EventRegistration::frameRateOverride);
+    mThrottledConnection = createConnection(mThrottledConnectionEventCallRecorder,
+                                            gui::ISurfaceComposer::EventRegistration::modeChanged,
+                                            mThrottledConnectionUid);
+
+    // A display must be connected for VSYNC events to be delivered.
+    mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
+    expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
-        ConnectionEventRecorder& recorder,
-        ISurfaceComposer::EventRegistrationFlags eventRegistration, uid_t ownerUid) {
+        ConnectionEventRecorder& recorder, EventRegistrationFlags eventRegistration,
+        uid_t ownerUid) {
     sp<MockEventThreadConnection> connection =
-            new MockEventThreadConnection(mThread.get(), ownerUid,
-                                          mResyncCallRecorder.getInvocable(), eventRegistration);
+            sp<MockEventThreadConnection>::make(mThread.get(), ownerUid,
+                                                mResyncCallRecorder.getInvocable(),
+                                                eventRegistration);
     EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
     return connection;
 }
 
-void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) {
-    auto args = mVSyncSetEnabledCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedState, std::get<0>(args.value()));
+void EventThreadTest::expectVSyncCallbackScheduleReceived(bool expectState) {
+    if (expectState) {
+        ASSERT_TRUE(mVSyncCallbackScheduleRecorder.waitForCall().has_value());
+    } else {
+        ASSERT_FALSE(mVSyncCallbackScheduleRecorder.waitForUnexpectedCall().has_value());
+    }
 }
 
 void EventThreadTest::expectVSyncSetDurationCallReceived(
         std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) {
-    auto args = mVSyncSetDurationCallRecorder.waitForCall();
+    auto args = mVSyncCallbackUpdateRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedDuration, std::get<0>(args.value()));
-    EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value()));
-}
-
-VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
-    auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
-    return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
-}
-
-void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) {
-    auto args = mInterceptVSyncCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+    EXPECT_EQ(expectedDuration.count(), std::get<1>(args.value()).workDuration);
+    EXPECT_EQ(expectedReadyDuration.count(), std::get<1>(args.value()).readyDuration);
 }
 
 void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
@@ -254,12 +253,12 @@
 }
 
 void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(
-        nsecs_t expectedTimestamp, VSyncSource::VSyncData preferredVsyncData) {
+        nsecs_t expectedTimestamp, VsyncEventData::FrameTimeline preferredVsyncData) {
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
                                   << expectedTimestamp;
     const auto& event = std::get<0>(args.value());
-    for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+    for (int i = 0; i < event.vsync.vsyncData.frameTimelinesLength; i++) {
         auto prediction = mTokenManager->getPredictionsForToken(
                 event.vsync.vsyncData.frameTimelines[i].vsyncId);
         EXPECT_TRUE(prediction.has_value());
@@ -293,6 +292,21 @@
     }
 }
 
+void EventThreadTest::expectVsyncEventDataFrameTimelinesValidLength(
+        VsyncEventData vsyncEventData, std::chrono::nanoseconds vsyncPeriod) {
+    float nonPreferredTimelinesAmount =
+            scheduler::VsyncConfig::kEarlyLatchMaxThreshold / vsyncPeriod;
+    EXPECT_LE(vsyncEventData.frameTimelinesLength, nonPreferredTimelinesAmount + 1)
+            << "Amount of non-preferred frame timelines too many;"
+            << " expected presentation time will be over threshold";
+    EXPECT_LT(nonPreferredTimelinesAmount, VsyncEventData::kFrameTimelinesCapacity)
+            << "Amount of non-preferred frame timelines should be less than max capacity";
+    EXPECT_GT(static_cast<int64_t>(vsyncEventData.frameTimelinesLength), 0)
+            << "Frame timelines length should be greater than 0";
+    EXPECT_LT(vsyncEventData.preferredFrameTimelineIndex, vsyncEventData.frameTimelinesLength)
+            << "Preferred frame timeline index should be less than frame timelines length";
+}
+
 void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                              bool expectedConnected) {
     auto args = mConnectionEventCallRecorder.waitForCall();
@@ -343,15 +357,19 @@
  */
 
 TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-    EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
+    setupEventThread(VSYNC_PERIOD);
+
+    EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackUpdateRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackUnregisterRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
 TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
     expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
 
@@ -359,69 +377,102 @@
     mThread->requestNextVsync(mConnection);
 
     // EventThread should not enable vsync callbacks.
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
+    setupEventThread(VSYNC_PERIOD);
+
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection);
 
     // EventThread should immediately request a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
-    // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    // EventThread should enable schedule a vsync callback
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, as well as the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The throttler should receive the event, as well as the connection.
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
+    // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback
+    expectVSyncCallbackScheduleReceived(true);
+
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    onVSyncEvent(456, 123, 0);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should also detect that at this point that it does not need
     // any more vsync events, and should disable their generation.
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+    setupEventThread(VSYNC_PERIOD);
+
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection);
 
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a vsync event.
-    // The interceptor should receive the event, as well as the connection.
-    VSyncSource::VSyncData vsyncData = {456, 789};
-    mCallback->onVSyncEvent(123, vsyncData);
-    expectInterceptCallReceived(123);
-    expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
+    // The throttler should receive the event, as well as the connection.
+    onVSyncEvent(123, 456, 789);
+    expectVsyncEventFrameTimelinesCorrect(123, {-1, 789, 456});
+}
+
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesValidLength) {
+    // The VsyncEventData should not have kFrameTimelinesCapacity amount of valid frame timelines,
+    // due to longer vsync period and kEarlyLatchMaxThreshold. Use length-2 to avoid decimal
+    // truncation (e.g. 60Hz has 16.6... ms vsync period).
+    std::chrono::nanoseconds vsyncPeriod(scheduler::VsyncConfig::kEarlyLatchMaxThreshold /
+                                         (VsyncEventData::kFrameTimelinesCapacity - 2));
+    setupEventThread(vsyncPeriod);
+
+    // Signal that we want the next vsync event to be posted to the connection
+    mThread->requestNextVsync(mConnection);
+
+    expectVSyncCallbackScheduleReceived(true);
+
+    // Use the received callback to signal a vsync event.
+    // The throttler should receive the event, as well as the connection.
+    nsecs_t expectedTimestamp = 123;
+    onVSyncEvent(expectedTimestamp, 456, 789);
+
+    auto args = mConnectionEventCallRecorder.waitForCall();
+    ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+                                  << expectedTimestamp;
+    const VsyncEventData vsyncEventData = std::get<0>(args.value()).vsync.vsyncData;
+    expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, vsyncPeriod);
 }
 
 TEST_F(EventThreadTest, getLatestVsyncEventData) {
+    setupEventThread(VSYNC_PERIOD);
+
     const nsecs_t now = systemTime();
-    const nsecs_t preferredDeadline = now + 10000000;
     const nsecs_t preferredExpectedPresentationTime = now + 20000000;
-    const VSyncSource::VSyncData preferredData = {preferredExpectedPresentationTime,
-                                                  preferredDeadline};
-    EXPECT_CALL(*mVSyncSource, getLatestVSyncData()).WillOnce(Return(preferredData));
+    const nsecs_t preferredDeadline = preferredExpectedPresentationTime - kReadyDuration.count();
+
+    mock::VSyncTracker& mockTracker =
+            *static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker());
+    EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .WillOnce(Return(preferredExpectedPresentationTime));
 
     VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
 
     // Check EventThread immediately requested a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
+    expectVsyncEventDataFrameTimelinesValidLength(vsyncEventData, VSYNC_PERIOD);
     EXPECT_GT(vsyncEventData.frameTimelines[0].deadlineTimestamp, now)
             << "Deadline timestamp should be greater than frame time";
-    for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+    for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
         auto prediction =
                 mTokenManager->getPredictionsForToken(vsyncEventData.frameTimelines[i].vsyncId);
         EXPECT_TRUE(prediction.has_value());
@@ -457,14 +508,15 @@
 }
 
 TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
+    setupEventThread(VSYNC_PERIOD);
+
     // Create a first connection, register it, and request a vsync rate of zero.
     ConnectionEventRecorder firstConnectionEventRecorder{0};
     sp<MockEventThreadConnection> firstConnection = createConnection(firstConnectionEventRecorder);
     mThread->setVsyncRate(0, firstConnection);
 
     // By itself, this should not enable vsync events
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-    EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+    expectVSyncCallbackScheduleReceived(false);
 
     // However if there is another connection which wants events at a nonzero rate.....
     ConnectionEventRecorder secondConnectionEventRecorder{0};
@@ -473,117 +525,118 @@
     mThread->setVsyncRate(1, secondConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the second connection. The first connection should not
+    // the second connection. The first connection should not
     // get the event.
-    mCallback->onVSyncEvent(123, {456, 0});
-    expectInterceptCallReceived(123);
+    onVSyncEvent(123, 0456, 0);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
                                          1u);
 }
 
 TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->setVsyncRate(1, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
-    // interceptor, and the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // throttler, and the connection.
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    onVSyncEvent(456, 123, 0);
     expectThrottleVsyncReceived(123, mConnectionUid);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    mCallback->onVSyncEvent(789, {777, 111});
-    expectInterceptCallReceived(789);
+    onVSyncEvent(789, 777, 111);
     expectThrottleVsyncReceived(777, mConnectionUid);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
 
 TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->setVsyncRate(2, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
-    // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The first event will not be seen by the connection.
+    onVSyncEvent(123, 456, 789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The second event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    // The second event will be seen by the connection.
+    onVSyncEvent(456, 123, 0);
     expectVsyncEventReceivedByConnection(456, 2u);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The third event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(789, {777, 744});
-    expectInterceptCallReceived(789);
+    // The third event will not be seen by the connection.
+    onVSyncEvent(789, 777, 744);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
-    // The fourth event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(101112, {7847, 86});
-    expectInterceptCallReceived(101112);
+    // The fourth event will be seen by the connection.
+    onVSyncEvent(101112, 7847, 86);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
 
 TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->setVsyncRate(1, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
-    // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The first event will not be seen by the connection.
+    onVSyncEvent(123, 56, 789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
+    setupEventThread(VSYNC_PERIOD);
+
     ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The first event will be seen by the connection, which then returns an error.
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
-    // A subsequent event will be seen by the interceptor and not by the
-    // connection.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    // Another schedule is expected, since the connection is removed only after
+    // the next vsync is requested.
+    expectVSyncCallbackScheduleReceived(true);
+
+    // A subsequent event will not be seen by the connection.
+    onVSyncEvent(456, 123, 0);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks with the second event
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, tracksEventConnections) {
+    setupEventThread(VSYNC_PERIOD);
+
     EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
     ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
@@ -596,12 +649,10 @@
     EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an error.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The first event will be seen by the connection, which then returns an error.
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
     expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -609,87 +660,110 @@
 }
 
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
+    setupEventThread(VSYNC_PERIOD);
+
     ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
     sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder);
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
-    // The first event will be seen by the interceptor, and by the connection,
-    // which then returns an non-fatal error.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The first event will be seen by the connection, which then returns a non-fatal error.
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+    expectVSyncCallbackScheduleReceived(true);
 
-    // A subsequent event will be seen by the interceptor, and by the connection,
-    // which still then returns an non-fatal error.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    // A subsequent event will be seen by the connection, which still then returns a non-fatal
+    // error.
+    onVSyncEvent(456, 123, 0);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
+    expectVSyncCallbackScheduleReceived(true);
 
     // EventThread will not disable vsync callbacks as the errors are non-fatal.
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    onVSyncEvent(456, 123, 0);
+    expectVSyncCallbackScheduleReceived(true);
 }
 
 TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->setDuration(321ns, 456ns);
     expectVSyncSetDurationCallReceived(321ns, 456ns);
 }
 
 TEST_F(EventThreadTest, postHotplugInternalDisconnect) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false);
     expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false);
 }
 
 TEST_F(EventThreadTest, postHotplugInternalConnect) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
     expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, true);
 }
 
 TEST_F(EventThreadTest, postHotplugExternalDisconnect) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, false);
     expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, false);
 }
 
 TEST_F(EventThreadTest, postHotplugExternalConnect) {
+    setupEventThread(VSYNC_PERIOD);
+
     mThread->onHotplugReceived(EXTERNAL_DISPLAY_ID, true);
     expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true);
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
+    setupEventThread(VSYNC_PERIOD);
+
     const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
                               .setPhysicalDisplayId(INTERNAL_DISPLAY_ID)
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
+    setupEventThread(VSYNC_PERIOD);
+
     const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
                               .setPhysicalDisplayId(EXTERNAL_DISPLAY_ID)
                               .setId(DisplayModeId(5))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
+    setupEventThread(VSYNC_PERIOD);
+
     const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
                               .setPhysicalDisplayId(DISPLAY_ID_64BIT)
                               .setId(DisplayModeId(7))
                               .setVsyncPeriod(16666666)
                               .build();
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+    const Fps fps = mode->getFps() / 2;
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
 }
 
 TEST_F(EventThreadTest, suppressConfigChanged) {
+    setupEventThread(VSYNC_PERIOD);
+
     ConnectionEventRecorder suppressConnectionEventRecorder{0};
     sp<MockEventThreadConnection> suppressConnection =
             createConnection(suppressConnectionEventRecorder);
@@ -699,15 +773,18 @@
                               .setId(DisplayModeId(9))
                               .setVsyncPeriod(16666666)
                               .build();
+    const Fps fps = mode->getFps() / 2;
 
-    mThread->onModeChanged(mode);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+    mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
 
     auto args = suppressConnectionEventRecorder.waitForCall();
     ASSERT_FALSE(args.has_value());
 }
 
 TEST_F(EventThreadTest, postUidFrameRateMapping) {
+    setupEventThread(VSYNC_PERIOD);
+
     const std::vector<FrameRateOverride> overrides = {
             {.uid = 1, .frameRateHz = 20},
             {.uid = 3, .frameRateHz = 40},
@@ -719,6 +796,8 @@
 }
 
 TEST_F(EventThreadTest, suppressUidFrameRateMapping) {
+    setupEventThread(VSYNC_PERIOD);
+
     const std::vector<FrameRateOverride> overrides = {
             {.uid = 1, .frameRateHz = 20},
             {.uid = 3, .frameRateHz = 40},
@@ -737,6 +816,8 @@
 }
 
 TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+    setupEventThread(VSYNC_PERIOD);
+
     // Signal that we want the next vsync event to be posted to the throttled connection
     mThread->requestNextVsync(mThrottledConnection);
 
@@ -744,26 +825,27 @@
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a first vsync event.
-    // The interceptor should receive the event, but not the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
-    expectInterceptCallReceived(123);
+    // The throttler should receive the event, but not the connection.
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mThrottledConnectionUid);
     mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a second vsync event.
-    // The interceptor should receive the event, but the connection should
+    // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456, {123, 0});
-    expectInterceptCallReceived(456);
+    onVSyncEvent(456, 123, 0);
     expectThrottleVsyncReceived(123, mThrottledConnectionUid);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+    expectVSyncCallbackScheduleReceived(true);
 
     // EventThread should not change the vsync state as it didn't send the event
     // yet
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    onVSyncEvent(456, 123, 0);
+    expectVSyncCallbackScheduleReceived(true);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index bb1f432..f695b09 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -24,17 +24,12 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "FpsReporter.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "fake/FakeClock.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
 #include "mock/MockFrameTimeline.h"
-#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -50,7 +45,7 @@
 using android::Hwc2::IComposer;
 using android::Hwc2::IComposerClient;
 
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using gui::LayerMetadata;
 
 struct TestableFpsListener : public gui::BnFpsListener {
     TestableFpsListener() {}
@@ -79,8 +74,7 @@
     static constexpr uint32_t LAYER_FLAGS = 0;
     static constexpr int32_t PRIORITY_UNSET = -1;
 
-    void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     mock::FrameTimeline mFrameTimeline =
@@ -95,8 +89,8 @@
 
     sp<TestableFpsListener> mFpsListener;
     fake::FakeClock* mClock = new fake::FakeClock();
-    sp<FpsReporter> mFpsReporter =
-            new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock));
+    sp<FpsReporter> mFpsReporter = sp<FpsReporter>::make(mFrameTimeline, *(mFlinger.flinger()),
+                                                         std::unique_ptr<Clock>(mClock));
 };
 
 FpsReporterTest::FpsReporterTest() {
@@ -104,10 +98,10 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    setupScheduler();
+    mFlinger.setupMockScheduler();
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
 
-    mFpsListener = new TestableFpsListener();
+    mFpsListener = sp<TestableFpsListener>::make();
 }
 
 FpsReporterTest::~FpsReporterTest() {
@@ -116,35 +110,10 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
+sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return new BufferStateLayer(args);
-}
-
-void FpsReporterTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread));
+    return sp<Layer>::make(args);
 }
 
 namespace {
@@ -153,7 +122,7 @@
     mParent = createBufferStateLayer();
     constexpr int32_t kTaskId = 12;
     LayerMetadata targetMetadata;
-    targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+    targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId);
     mTarget = createBufferStateLayer(targetMetadata);
     mChild = createBufferStateLayer();
     mGrandChild = createBufferStateLayer();
@@ -188,7 +157,7 @@
 TEST_F(FpsReporterTest, rateLimits) {
     const constexpr int32_t kTaskId = 12;
     LayerMetadata targetMetadata;
-    targetMetadata.setInt32(METADATA_TASK_ID, kTaskId);
+    targetMetadata.setInt32(gui::METADATA_TASK_ID, kTaskId);
     mTarget = createBufferStateLayer(targetMetadata);
     mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget);
 
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
rename to services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
index 1e6e336..1c9aee7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
@@ -21,13 +21,9 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -40,8 +36,6 @@
 using android::Hwc2::IComposer;
 using android::Hwc2::IComposerClient;
 
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
 /**
  * This class covers all the test that are related to refresh rate selection.
  */
@@ -58,9 +52,8 @@
     static constexpr uint32_t LAYER_FLAGS = 0;
     static constexpr int32_t PRIORITY_UNSET = -1;
 
-    void setupScheduler();
-    sp<BufferStateLayer> createBufferStateLayer();
-    sp<EffectLayer> createEffectLayer();
+    sp<Layer> createBufferStateLayer();
+    sp<Layer> createEffectLayer();
 
     void setParent(Layer* child, Layer* parent);
     void commitTransaction(Layer* layer);
@@ -78,7 +71,7 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    setupScheduler();
+    mFlinger.setupMockScheduler();
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
 }
 
@@ -88,22 +81,21 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-
-sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return new BufferStateLayer(args);
+    return sp<Layer>::make(args);
 }
 
-sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
+sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return new EffectLayer(args);
+    return sp<Layer>::make(args);
 }
 
 void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
-    child->setParent(parent);
+    child->setParent(sp<Layer>::fromExisting(parent));
 }
 
 void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
@@ -111,35 +103,8 @@
     layer->commitTransaction(c);
 }
 
-void RefreshRateSelectionTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread));
-}
-
 namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
 TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
     mParent = createBufferStateLayer();
     mChild = createBufferStateLayer();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index f1efa92..d26ef3c 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -44,6 +44,17 @@
 
 namespace android::frametimeline {
 
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
+
 class FrameTimelineTest : public testing::Test {
 public:
     FrameTimelineTest() {
@@ -106,6 +117,14 @@
         return packets;
     }
 
+    void addEmptySurfaceFrame() {
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne,
+                                                           /*isBuffer*/ false, sGameMode);
+        mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame));
+    }
+
     void addEmptyDisplayFrame() {
         auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
         // Trigger a flushPresentFence by calling setSfPresent for the next frame
@@ -168,17 +187,6 @@
                                                                   kStartThreshold};
 };
 
-static const std::string sLayerNameOne = "layer1";
-static const std::string sLayerNameTwo = "layer2";
-
-constexpr const uid_t sUidOne = 0;
-constexpr pid_t sPidOne = 10;
-constexpr pid_t sPidTwo = 20;
-constexpr int32_t sInputEventId = 5;
-constexpr int32_t sLayerIdOne = 1;
-constexpr int32_t sLayerIdTwo = 2;
-constexpr GameMode sGameMode = GameMode::Unsupported;
-
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     EXPECT_EQ(getPredictions().size(), 1u);
@@ -217,9 +225,12 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     flushTokens();
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
@@ -227,9 +238,12 @@
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
@@ -239,9 +253,12 @@
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     constexpr int32_t inputEventId = 1;
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = inputEventId;
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
@@ -250,9 +267,12 @@
 TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     // Set up the display frame
@@ -278,14 +298,17 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdTwo, sLayerNameTwo,
-                                                       sLayerNameTwo, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdTwo,
+                                                       sLayerNameTwo, sLayerNameTwo,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -324,9 +347,11 @@
                 {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
         int64_t sfToken = mTokenManager->generateTokenForPredictions(
                 {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = surfaceFrameToken;
+        ftInfo.inputEventId = sInputEventId;
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
-                                                           sPidOne, sUidOne, sLayerIdOne,
+                mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
                                                            sLayerNameOne, sLayerNameOne,
                                                            /*isBuffer*/ true, sGameMode);
         mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
@@ -347,10 +372,13 @@
             {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
     int64_t sfToken = mTokenManager->generateTokenForPredictions(
             {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -442,11 +470,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -457,8 +488,8 @@
     addEmptyDisplayFrame();
 
     auto displayFrame0 = getDisplayFrame(0);
-    EXPECT_EQ(displayFrame0->getActuals().presentTime, -1);
-    EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown);
+    EXPECT_EQ(displayFrame0->getActuals().presentTime, 59);
+    EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown | JankType::DisplayHAL);
     EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1);
     EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
 }
@@ -470,11 +501,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = -1;
     int64_t sfToken1 = -1;
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -495,11 +529,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -521,11 +558,14 @@
     auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setAcquireFenceTime(20);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -546,11 +586,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     surfaceFrame1->setAcquireFenceTime(20);
@@ -570,11 +613,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -596,11 +642,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({40, 60, 92});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -622,11 +671,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -648,11 +700,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({30, 40, 58});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
 
@@ -676,11 +731,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(45);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
 
@@ -706,11 +764,14 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
 
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(45);
     // Trigger a prediction expiry
     flushTokens();
@@ -744,9 +805,12 @@
     auto tracingSession = getTracingSessionForTest();
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     // Set up the display frame
@@ -771,9 +835,12 @@
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = token1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne,
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
                                                        /*isBuffer*/ true, sGameMode);
 
     // Set up the display frame
@@ -995,6 +1062,9 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
     tracingSession->StartBlocking();
+
+    // Add an empty surface frame so that display frame would get traced.
+    addEmptySurfaceFrame();
     int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
 
     // Set up the display frame
@@ -1076,6 +1146,9 @@
     // Flush the token so that it would expire
     flushTokens();
 
+    // Add an empty surface frame so that display frame would get traced.
+    addEmptySurfaceFrame();
+
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
     mFrameTimeline->setSfPresent(26, presentFence1);
@@ -1133,14 +1206,18 @@
     int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 25, 40});
     int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({30, 35, 40});
 
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken;
+    ftInfo.inputEventId = sInputEventId;
+
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setActualQueueTime(10);
     surfaceFrame1->setDropTime(15);
 
@@ -1293,10 +1370,13 @@
 
     // Flush the token so that it would expire
     flushTokens();
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken;
+    ftInfo.inputEventId = 0;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
-                                                       sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setActualQueueTime(appEndTime);
     surfaceFrame1->setAcquireFenceTime(appEndTime);
 
@@ -1369,10 +1449,13 @@
 
     // Flush the token so that it would expire
     flushTokens();
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken;
+    ftInfo.inputEventId = 0;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
-                                                       sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
 
     constexpr nsecs_t sfStartTime = std::chrono::nanoseconds(22ms).count();
     constexpr nsecs_t sfEndTime = std::chrono::nanoseconds(30ms).count();
@@ -1438,10 +1521,13 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 30});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1608,7 +1694,7 @@
     EXPECT_EQ(displayFrame0->getActuals().presentTime, 52);
     EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
     EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
-    EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+    EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
 
     // case 3 - cpu time = 86 - 82 = 4, vsync period = 30
     mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30));
@@ -1654,10 +1740,13 @@
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1674,10 +1763,13 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    FrameTimelineInfo ftInfo2;
+    ftInfo2.vsyncId = surfaceFrameToken2;
+    ftInfo2.inputEventId = sInputEventId;
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1734,10 +1826,13 @@
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 70});
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1754,10 +1849,13 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    FrameTimelineInfo ftInfo2;
+    ftInfo2.vsyncId = surfaceFrameToken2;
+    ftInfo2.inputEventId = sInputEventId;
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1813,10 +1911,13 @@
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 50, 50});
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1857,10 +1958,13 @@
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 50, 50});
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(26);
     mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1877,10 +1981,13 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    FrameTimelineInfo ftInfo2;
+    ftInfo2.vsyncId = surfaceFrameToken2;
+    ftInfo2.inputEventId = sInputEventId;
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame2->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1932,10 +2039,13 @@
 
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 120, 120});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1952,10 +2062,13 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    FrameTimelineInfo ftInfo2;
+    ftInfo2.vsyncId = surfaceFrameToken2;
+    ftInfo2.inputEventId = sInputEventId;
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame2->setAcquireFenceTime(84);
     mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -2010,10 +2123,13 @@
 
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 60, 60});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 90});
+    FrameTimelineInfo ftInfo;
+    ftInfo.vsyncId = surfaceFrameToken1;
+    ftInfo.inputEventId = sInputEventId;
     auto surfaceFrame1 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -2030,10 +2146,13 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    FrameTimelineInfo ftInfo2;
+    ftInfo2.vsyncId = surfaceFrameToken2;
+    ftInfo2.inputEventId = sInputEventId;
     auto surfaceFrame2 =
-            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerIdOne, sLayerNameOne,
-                                                       sLayerNameOne, /*isBuffer*/ true, sGameMode);
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
     surfaceFrame2->setAcquireFenceTime(80);
     mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
     // Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -2079,6 +2198,95 @@
     EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing);
 }
 
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_GpuAndCpuMiss) {
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+
+    // Case 1: cpu time = 33 - 12 = 21, vsync period = 11
+    mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1);
+    auto displayFrame = getDisplayFrame(0);
+    gpuFence1->signalForTest(36);
+    presentFence1->signalForTest(52);
+
+    // Fences haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+    displayFrame = getDisplayFrame(0);
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+    EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed);
+
+    // Case 2: No GPU fence so it will not use GPU composition.
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30));
+    mFrameTimeline->setSfPresent(66, presentFence2);
+    auto displayFrame2 = getDisplayFrame(2); // 2 because of previous empty frame
+    presentFence2->signalForTest(90);
+
+    // Fences for the frame haven't been flushed yet, so it should be 0
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+    addEmptyDisplayFrame();
+
+    // Fences have flushed, so the present timestamps should be updated
+    EXPECT_EQ(displayFrame2->getActuals().presentTime, 90);
+    EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+    EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+    EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_presentFenceError) {
+    auto erroneousPresentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto erroneousPresentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    auto validPresentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+    int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60});
+    int64_t sfToken3 = mTokenManager->generateTokenForPredictions({72, 80, 80});
+
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(26, erroneousPresentFence1);
+
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(60, erroneousPresentFence2);
+
+    mFrameTimeline->setSfWakeUp(sfToken3, 72, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfPresent(80, validPresentFence);
+
+    erroneousPresentFence2->signalForTest(2);
+    validPresentFence->signalForTest(80);
+
+    addEmptyDisplayFrame();
+
+    {
+        auto displayFrame = getDisplayFrame(0);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 26);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
+    }
+    {
+        auto displayFrame = getDisplayFrame(1);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 60);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL);
+    }
+    {
+        auto displayFrame = getDisplayFrame(2);
+        EXPECT_EQ(displayFrame->getActuals().presentTime, 80);
+        EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+        EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+        EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+    }
+}
+
 TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
     EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
 }
@@ -2237,4 +2445,38 @@
     EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
 }
 
+TEST_F(FrameTimelineTest, getMinTime) {
+    // Use SurfaceFrame::getBaseTime to test the getMinTime.
+    FrameTimelineInfo ftInfo;
+
+    // Valid prediction state test.
+    ftInfo.vsyncId = 0L;
+    mTokenManager->generateTokenForPredictions({10});
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne,
+                                                       /*isBuffer*/ true, sGameMode);
+    ASSERT_EQ(surfaceFrame->getBaseTime(), 10);
+
+    // Test prediction state which is not valid.
+    ftInfo.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
+    surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+                                                              sLayerNameOne, sLayerNameOne,
+                                                              /*isBuffer*/ true, sGameMode);
+    // Start time test.
+    surfaceFrame->setActualStartTime(200);
+    ASSERT_EQ(surfaceFrame->getBaseTime(), 200);
+
+    // End time test.
+    surfaceFrame->setAcquireFenceTime(100);
+    ASSERT_EQ(surfaceFrame->getBaseTime(), 100);
+
+    // Present time test.
+    auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame);
+    presentFence->signalForTest(std::chrono::nanoseconds(50ns).count());
+    mFrameTimeline->setSfPresent(50, presentFence);
+    ASSERT_EQ(surfaceFrame->getBaseTime(), 50);
+}
 } // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index 981ca1d..1b5c6e7 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -25,15 +25,15 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
 
 namespace android {
 
 using testing::_;
 using testing::Mock;
 using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+using gui::GameMode;
+using gui::LayerMetadata;
 
 class GameModeTest : public testing::Test {
 public:
@@ -41,7 +41,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-        setupScheduler();
+        mFlinger.setupMockScheduler();
         setupComposer();
     }
 
@@ -51,36 +51,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return new BufferStateLayer(args);
-    }
-
-    void setupScheduler() {
-        auto eventThread = std::make_unique<mock::EventThread>();
-        auto sfEventThread = std::make_unique<mock::EventThread>();
-
-        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        auto vsyncController = std::make_unique<mock::VsyncController>();
-        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*vsyncTracker, currentPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                std::move(eventThread), std::move(sfEventThread));
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void setupComposer() {
@@ -92,7 +66,7 @@
 
     // Mocks the behavior of applying a transaction from WMShell
     void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
-        mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
+        mLayerMetadata.setInt32(gui::METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
         layer->setMetadata(mLayerMetadata);
         layer->setGameModeForTree(gameMode);
     }
@@ -104,9 +78,9 @@
 };
 
 TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->addChild(childLayer1);
     rootLayer->addChild(childLayer2);
     rootLayer->setGameModeForTree(GameMode::Performance);
@@ -117,8 +91,8 @@
 }
 
 TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -127,8 +101,8 @@
 }
 
 TEST_F(GameModeTest, RemoveChildResetsGameMode) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer = createLayer();
     rootLayer->setGameModeForTree(GameMode::Performance);
     rootLayer->addChild(childLayer);
 
@@ -140,9 +114,9 @@
 }
 
 TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
-    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
-    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    sp<Layer> rootLayer = createLayer();
+    sp<Layer> childLayer1 = createLayer();
+    sp<Layer> childLayer2 = createLayer();
     rootLayer->setGameModeForTree(GameMode::Standard);
     rootLayer->addChild(childLayer1);
 
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 5241604..da00377 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -52,6 +52,7 @@
 
 using Hwc2::Config;
 
+using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::ElementsAreArray;
@@ -59,27 +60,91 @@
 using ::testing::SetArgPointee;
 using ::testing::StrictMock;
 
-TEST(HWComposerTest, isHeadless) {
-    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
-    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    ASSERT_TRUE(hwc.isHeadless());
+struct HWComposerTest : testing::Test {
+    using HalError = hardware::graphics::composer::V2_1::Error;
 
-    const hal::HWDisplayId hwcId = 1;
+    Hwc2::mock::Composer* const mHal = new StrictMock<Hwc2::mock::Composer>();
+    impl::HWComposer mHwc{std::unique_ptr<Hwc2::Composer>(mHal)};
 
-    EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
-            .WillOnce(DoAll(SetArgPointee<2>(getExternalEdid()),
-                            Return(hardware::graphics::composer::V2_1::Error::NONE)));
+    void expectHotplugConnect(hal::HWDisplayId hwcDisplayId) {
+        constexpr uint8_t kPort = 255;
+        EXPECT_CALL(*mHal, getDisplayIdentificationData(hwcDisplayId, _, _))
+                .WillOnce(DoAll(SetArgPointee<1>(kPort),
+                                SetArgPointee<2>(getExternalEdid()), Return(HalError::NONE)));
 
-    EXPECT_CALL(*mHal, setVsyncEnabled(_, _));
-    EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+        EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+        EXPECT_CALL(*mHal, setVsyncEnabled(hwcDisplayId, Hwc2::IComposerClient::Vsync::DISABLE));
+        EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId));
+    }
+};
 
-    auto info = hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+TEST_F(HWComposerTest, isHeadless) {
+    ASSERT_TRUE(mHwc.isHeadless());
+
+    constexpr hal::HWDisplayId kHwcDisplayId = 1;
+    expectHotplugConnect(kHwcDisplayId);
+
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
     ASSERT_TRUE(info);
-    auto displayId = info->id;
-    ASSERT_FALSE(hwc.isHeadless());
 
-    hwc.disconnectDisplay(displayId);
-    ASSERT_TRUE(hwc.isHeadless());
+    ASSERT_FALSE(mHwc.isHeadless());
+
+    mHwc.disconnectDisplay(info->id);
+    ASSERT_TRUE(mHwc.isHeadless());
+}
+
+TEST_F(HWComposerTest, getActiveMode) {
+    // Unknown display.
+    EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt);
+
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    expectHotplugConnect(kHwcDisplayId);
+
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    {
+        // Display is known to SF but not HWC, e.g. the hotplug disconnect is pending.
+        EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
+                .WillOnce(Return(HalError::BAD_DISPLAY));
+
+        EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt);
+    }
+    {
+        constexpr hal::HWConfigId kConfigId = 42;
+        EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _))
+                .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE)));
+
+        EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId);
+    }
+}
+
+TEST_F(HWComposerTest, onVsync) {
+    constexpr hal::HWDisplayId kHwcDisplayId = 1;
+    expectHotplugConnect(kHwcDisplayId);
+
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    const auto physicalDisplayId = info->id;
+
+    // Deliberately chosen not to match DisplayData.lastPresentTimestamp's
+    // initial value.
+    constexpr nsecs_t kTimestamp = 1;
+    auto displayIdOpt = mHwc.onVsync(kHwcDisplayId, kTimestamp);
+    ASSERT_TRUE(displayIdOpt);
+    EXPECT_EQ(physicalDisplayId, displayIdOpt);
+
+    // Attempt to send the same time stamp again.
+    displayIdOpt = mHwc.onVsync(kHwcDisplayId, kTimestamp);
+    EXPECT_FALSE(displayIdOpt);
+}
+
+TEST_F(HWComposerTest, onVsyncInvalid) {
+    constexpr hal::HWDisplayId kInvalidHwcDisplayId = 2;
+    constexpr nsecs_t kTimestamp = 1;
+    const auto displayIdOpt = mHwc.onVsync(kInvalidHwcDisplayId, kTimestamp);
+    EXPECT_FALSE(displayIdOpt);
 }
 
 struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
@@ -91,10 +156,10 @@
                  void(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&));
     MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
     MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
+    MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override));
 };
 
-struct HWComposerSetCallbackTest : testing::Test {
-    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+struct HWComposerSetCallbackTest : HWComposerTest {
     MockHWC2ComposerCallback mCallback;
 };
 
@@ -111,12 +176,14 @@
                                     {kMetadata2Name, kMetadata2Mandatory},
                             }),
                             Return(hardware::graphics::composer::V2_4::Error::NONE)));
+    EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::NONE));
+    EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::NONE));
+
     EXPECT_CALL(*mHal, registerCallback(_));
 
-    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(mCallback);
+    mHwc.setCallback(mCallback);
 
-    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    const auto& supported = mHwc.getSupportedLayerGenericMetadata();
     EXPECT_EQ(2u, supported.size());
     EXPECT_EQ(1u, supported.count(kMetadata1Name));
     EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second);
@@ -128,13 +195,14 @@
     EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<aidl::Capability>{}));
     EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
             .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, getOverlaySupport(_)).WillOnce(Return(HalError::UNSUPPORTED));
+    EXPECT_CALL(*mHal, getHdrConversionCapabilities(_)).WillOnce(Return(HalError::UNSUPPORTED));
     EXPECT_CALL(*mHal, registerCallback(_));
 
-    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
-    hwc.setCallback(mCallback);
+    mHwc.setCallback(mCallback);
 
-    const auto& supported = hwc.getSupportedLayerGenericMetadata();
-    EXPECT_EQ(0u, supported.size());
+    const auto& supported = mHwc.getSupportedLayerGenericMetadata();
+    EXPECT_TRUE(supported.empty());
 }
 
 struct HWComposerLayerTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..e4f49e8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,739 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "LayerHierarchyTest.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY)  \
+    ({                                \
+        SCOPED_TRACE("");             \
+        updateAndVerify((HIERARCHY)); \
+    })
+
+namespace android::surfaceflinger::frontend {
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+    /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+    --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+    --gtest_shuffle \
+    --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public LayerHierarchyTestBase {
+protected:
+    LayerHierarchyTest() : LayerHierarchyTestBase() { mLifecycleManager.commitChanges(); }
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    createRootLayer(3);
+    createLayer(112, 11);
+    createLayer(12211, 1221);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(2, 11);
+    reparentLayer(111, 12);
+    reparentLayer(1221, 1);
+    reparentLayer(1221, 13);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(1221, 13);
+    reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {2, 11, 111, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+    destroyLayerHandle(2);
+    destroyLayerHandle(11);
+    destroyLayerHandle(1221);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    destroyLayerHandle(111);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    // handle is destroyed but layer is kept alive and reachable by parent
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // destroy parent layer and the child gets destroyed
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    reparentLayer(11, 1);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(11, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    createLayer(112, 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111, 112};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    // This calls setLayer
+    removeRelativeZ(11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentRelativeLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(11, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    reparentLayer(11, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    setZ(11, 0);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 12, 121, 122,
+                                                   1221, 13, 14,  11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentLayer(11, UNASSIGNED_LAYER_ID);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    createLayer(1111, 111);
+    createLayer(112, 11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 1111, 112, 12,   121, 122,
+                                                   1221, 13, 14,  11,   111, 1111, 112, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(111, 12);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   ├── 122
+    // │   │   │   └── 1221
+    // │   │   └ - 111 (relative)
+    // │   ├── 13
+    // │   └── 14
+    // │       └ * 11 (mirroring)
+    // └── 2
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {1,    11, 111, 12, 111, 121, 122,
+                                                   1221, 13, 14,  11, 111, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    // 111 is not reachable in the mirror
+    expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(1221, 12);
+    mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   ├── 122
+    // │   │   │   └── 1221
+    // │   │   └ - 1221 (relative)
+    // │   ├── 13
+    // │   └── 14
+    // │       └ * 12 (mirroring)
+    // └── 2
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1,  11, 111, 12,  121, 122,  1221, 1221,
+                                                   13, 14, 12,  121, 122, 1221, 1221, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    // relative layer 1221 is traversable in the mirrored hierarchy as well
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    reparentRelativeLayer(11, 2);
+    reparentLayer(2, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(2);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // remove relative parent so layer becomes onscreen again
+    removeRelativeZ(11);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentRelativeLayer(1221, 2);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    // verify relz path
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // offscreen layer cannot be reached as a relative child
+    reparentLayer(12, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {12, 121, 122, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // layer when onscreen can be reached as a relative child again
+    reparentLayer(12, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    updateBackgroundColor(1, 0.5);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    auto hierarchy = hierarchyBuilder.getPartialHierarchy(1, /*childrenOnly=*/true);
+    auto bgLayerId = hierarchy.mChildren.front().first->getLayer()->id;
+    std::vector<uint32_t> expectedTraversalPath = {1,   bgLayerId, 11,   111, 12,
+                                                   121, 122,       1221, 13,  2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {bgLayerId, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createLayer(11, 1);
+    reparentLayer(1, 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    std::vector<uint32_t> expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    reparentRelativeLayer(11, 2);
+    reparentRelativeLayer(2, 11);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    // fix loop
+    uint32_t invalidRelativeRoot;
+    bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+    EXPECT_TRUE(hasRelZLoop);
+    mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+    hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+    EXPECT_EQ(invalidRelativeRoot, 11u);
+    EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    createLayer(111, 11);
+    createLayer(21, 2);
+    createLayer(22, 2);
+    createLayer(221, 22);
+    reparentRelativeLayer(22, 111);
+    reparentRelativeLayer(11, 221);
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+    // fix loop
+    uint32_t invalidRelativeRoot;
+    bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+    EXPECT_TRUE(hasRelZLoop);
+    mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+    hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+    EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 2, 21};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {11, 111, 22, 221};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    reparentLayer(1, UNASSIGNED_LAYER_ID);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    createRootLayer(1);
+    destroyLayerHandle(1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+    setZ(122, -1);
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    auto checkTraversalPathIdVisitor =
+            [](const LayerHierarchy& hierarchy,
+               const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+        EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+        return true;
+    };
+    hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+    hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerSequenceId) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createRootLayer(4);
+    createRootLayer(5);
+    createLayer(11, 1);
+    createLayer(51, 5);
+    createLayer(53, 5);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+    // A new layer is added with a smaller sequence id. Make sure its sorted correctly. While
+    // sequence ids are always incremented, this scenario can happen when a layer is reparented.
+    createRootLayer(3);
+    createLayer(52, 5);
+
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    expectedTraversalPath = {1, 11, 2, 3, 4, 5, 51, 52, 53};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerZ) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createLayer(11, 1);
+    createLayer(12, 1);
+    createLayer(13, 1);
+    setZ(11, -1);
+    setZ(12, 2);
+    setZ(13, 1);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    expectedTraversalPath = {11, 1, 13, 12};
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) {
+    // remove default hierarchy
+    mLifecycleManager = LayerLifecycleManager();
+    createRootLayer(1);
+    createRootLayer(2);
+    createLayer(11, 1);
+    createLayer(21, 2);
+    setLayerStack(1, 20);
+    setLayerStack(2, 10);
+
+    mLifecycleManager.commitChanges();
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+    std::vector<uint32_t> expectedTraversalPath = {2, 21, 1, 11};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+    expectedTraversalPath = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorDisplay) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3,
+                                      1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    createRootLayer(4);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 4, 3,
+                                      1, 11, 111, 12, 121, 122, 1221, 13, 2, 4};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) {
+    LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    reparentLayer(1, UNASSIGNED_LAYER_ID);
+    destroyLayerHandle(1);
+    UPDATE_AND_VERIFY(hierarchyBuilder);
+
+    std::vector<uint32_t> expected = {2, 3, 2};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+    EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+    expected = {11, 111, 12, 121, 122, 1221, 13};
+    EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
new file mode 100644
index 0000000..4301186
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -0,0 +1,314 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Client.h" // temporarily needed for LayerCreationArgs
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+
+namespace android::surfaceflinger::frontend {
+
+class LayerHierarchyTestBase : public testing::Test {
+protected:
+    LayerHierarchyTestBase() {
+        // tree with 3 levels of children
+        // ROOT
+        // ├── 1
+        // │   ├── 11
+        // │   │   └── 111
+        // │   ├── 12
+        // │   │   ├── 121
+        // │   │   └── 122
+        // │   │       └── 1221
+        // │   └── 13
+        // └── 2
+
+        createRootLayer(1);
+        createRootLayer(2);
+        createLayer(11, 1);
+        createLayer(12, 1);
+        createLayer(13, 1);
+        createLayer(111, 11);
+        createLayer(121, 12);
+        createLayer(122, 12);
+        createLayer(1221, 122);
+    }
+
+    LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId,
+                                 uint32_t layerIdToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
+        args.addToRoot = canBeRoot;
+        args.parentId = parentId;
+        args.layerIdToMirror = layerIdToMirror;
+        return args;
+    }
+
+    LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) {
+        LayerCreationArgs args(std::make_optional(id));
+        args.name = "testlayer";
+        args.addToRoot = true;
+        args.layerStackToMirror = layerStackToMirror;
+        return args;
+    }
+
+    std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+        std::vector<uint32_t> layerIds;
+        hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+                                                  const LayerHierarchy::TraversalPath&) -> bool {
+            layerIds.emplace_back(hierarchy.getLayer()->id);
+            return true;
+        });
+        return layerIds;
+    }
+
+    std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+        std::vector<uint32_t> layerIds;
+        hierarchy.traverseInZOrder(
+                [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+                                       const LayerHierarchy::TraversalPath&) -> bool {
+                    layerIds.emplace_back(hierarchy.getLayer()->id);
+                    return true;
+                });
+        return layerIds;
+    }
+
+    virtual void createRootLayer(uint32_t id) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createDisplayMirrorArgs(/*id=*/id, layerStack)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    virtual void createLayer(uint32_t id, uint32_t parentId) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/UNASSIGNED_LAYER_ID)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    std::vector<TransactionState> reparentLayerTransaction(uint32_t id, uint32_t newParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().parentId = newParentId;
+        transactions.back().states.front().state.what = layer_state_t::eReparent;
+        transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID;
+        transactions.back().states.front().layerId = id;
+        return transactions;
+    }
+
+    void reparentLayer(uint32_t id, uint32_t newParentId) {
+        mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId));
+    }
+
+    std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().relativeParentId = relativeParentId;
+        transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+        transactions.back().states.front().layerId = id;
+        return transactions;
+    }
+
+    void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+        mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId));
+    }
+
+    void removeRelativeZ(uint32_t id) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        layers.emplace_back(std::make_unique<RequestedLayerState>(
+                createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId,
+                           /*mirror=*/layerIdToMirror)));
+        mLifecycleManager.addLayers(std::move(layers));
+    }
+
+    void updateBackgroundColor(uint32_t id, half alpha) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+        transactions.back().states.front().state.bgColor.a = alpha;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+    void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+        if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+            hierarchyBuilder.update(mLifecycleManager.getLayers(),
+                                    mLifecycleManager.getDestroyedLayers());
+        }
+        mLifecycleManager.commitChanges();
+
+        // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+        LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+        EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+                  getTraversalPath(newBuilder.getHierarchy()));
+        EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+                  getTraversalPathInZOrder(newBuilder.getHierarchy()));
+        EXPECT_FALSE(
+                mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    }
+
+    std::vector<TransactionState> setZTransaction(uint32_t id, int32_t z) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.z = z;
+        return transactions;
+    }
+
+    void setZ(uint32_t id, int32_t z) {
+        mLifecycleManager.applyTransactions(setZTransaction(id, z));
+    }
+
+    void setCrop(uint32_t id, const Rect& crop) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eCropChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.crop = crop;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eFlagsChanged;
+        transactions.back().states.front().state.flags = flags;
+        transactions.back().states.front().state.mask = mask;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setAlpha(uint32_t id, float alpha) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eAlphaChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.color.a = static_cast<half>(alpha);
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void hideLayer(uint32_t id) {
+        setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden);
+    }
+
+    void showLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, 0); }
+
+    void setColor(uint32_t id, half3 rgb = half3(1._hf, 1._hf, 1._hf)) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+        transactions.back().states.front().state.what = layer_state_t::eColorChanged;
+        transactions.back().states.front().state.color.rgb = rgb;
+        transactions.back().states.front().layerId = id;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setLayerStack(uint32_t id, int32_t layerStack) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setTouchableRegion(uint32_t id, Region region) {
+        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 = id;
+        transactions.back().states.front().state.windowInfoHandle =
+                sp<gui::WindowInfoHandle>::make();
+        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        inputInfo->touchableRegion = region;
+        inputInfo->token = sp<BBinder>::make();
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
+                                bool replaceTouchableRegionWithCrop) {
+        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 = id;
+        transactions.back().states.front().state.windowInfoHandle =
+                sp<gui::WindowInfoHandle>::make();
+        auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+        inputInfo->touchableRegion = region;
+        inputInfo->replaceTouchableRegionWithCrop = replaceTouchableRegionWithCrop;
+        transactions.back().states.front().touchCropId = touchCropId;
+
+        inputInfo->token = sp<BBinder>::make();
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    void setBackgroundBlurRadius(uint32_t id, uint32_t backgroundBlurRadius) {
+        std::vector<TransactionState> transactions;
+        transactions.emplace_back();
+        transactions.back().states.push_back({});
+
+        transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+        transactions.back().states.front().layerId = id;
+        transactions.back().states.front().state.backgroundBlurRadius = backgroundBlurRadius;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
+    LayerLifecycleManager mLifecycleManager;
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 17511cd..85d86a7 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -51,8 +51,6 @@
     static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
     static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
     static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
-    static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
-            LayerInfo::RefreshRateHistory::HISTORY_DURATION;
 
     static constexpr Fps LO_FPS = 30_Hz;
     static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
@@ -69,11 +67,11 @@
         // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
         // however, for testing only, a stable order is required, therefore we sort the list here.
         // Any tests requiring ordered results must create layers with names.
-        auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+        auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
         std::sort(summary.begin(), summary.end(),
-                  [](const RefreshRateConfigs::LayerRequirement& a,
-                     const RefreshRateConfigs::LayerRequirement& b) -> bool {
-                      return a.name < b.name;
+                  [](const RefreshRateSelector::LayerRequirement& lhs,
+                     const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+                      return lhs.name < rhs.name;
                   });
         return summary;
     }
@@ -86,7 +84,7 @@
     auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
         const auto& infos = history().mActiveLayerInfos;
         return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
-            return pair.second.second->isFrequent(now);
+            return pair.second.second->isFrequent(now).isFrequent;
         });
     }
 
@@ -97,6 +95,13 @@
         });
     }
 
+    auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mActiveLayerInfos;
+        return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+            return pair.second.second->isFrequent(now).clearHistory;
+        });
+    }
+
     void setDefaultLayerVote(Layer* layer,
                              LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
         auto [found, layerPair] = history().findLayer(layer->getSequence());
@@ -114,7 +119,8 @@
                                Fps desiredRefreshRate, int numFrames) {
         LayerHistory::Summary summary;
         for (int i = 0; i < numFrames; i++) {
-            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
             time += frameRate.getPeriodNsecs();
 
             summary = summarizeLayerHistory(time);
@@ -125,22 +131,72 @@
         ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
     }
 
-    std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(createDisplayMode(DisplayModeId(0),
-                                                                             LO_FPS),
-                                                           createDisplayMode(DisplayModeId(1),
-                                                                             HI_FPS)),
-                                                 DisplayModeId(0));
+    std::shared_ptr<RefreshRateSelector> mSelector =
+            std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+                                                                              LO_FPS),
+                                                            createDisplayMode(DisplayModeId(1),
+                                                                              HI_FPS)),
+                                                  DisplayModeId(0));
 
     mock::SchedulerCallback mSchedulerCallback;
 
-    TestableScheduler* mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
+    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
 
     TestableSurfaceFlinger mFlinger;
 };
 
 namespace {
 
+TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
+            .WillOnce(Return(LayerInfo::FrameRateCompatibility::NoVote));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(1, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+    EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
+            .WillOnce(Return(LayerInfo::FrameRateCompatibility::Min));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+}
+
 TEST_F(LayerHistoryTest, oneLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -159,7 +215,8 @@
 
     // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -168,7 +225,8 @@
 
     // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         ASSERT_EQ(1, summarizeLayerHistory(time).size());
         EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
@@ -186,7 +244,8 @@
 
     nsecs_t time = systemTime();
 
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     auto summary = summarizeLayerHistory(time);
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
     // Layer is still considered inactive so we expect to get Min
@@ -194,7 +253,8 @@
     EXPECT_EQ(1, activeLayerCount());
 
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     summary = summarizeLayerHistory(time);
     EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -211,7 +271,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -234,7 +295,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -261,7 +323,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -289,7 +352,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
@@ -317,7 +381,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -349,7 +414,8 @@
 
     nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
     }
 
@@ -395,7 +461,8 @@
 
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
         summary = summarizeLayerHistory(time);
     }
@@ -407,13 +474,15 @@
 
     // layer2 is frequent and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
 
     // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
 
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
@@ -426,7 +495,8 @@
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
     for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -442,10 +512,12 @@
     constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
         if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
         }
 
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -458,7 +530,8 @@
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
-    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     summary = summarizeLayerHistory(time);
     ASSERT_EQ(2, summary.size());
     EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
@@ -484,7 +557,8 @@
     // layer2 still has low refresh rate.
     // layer3 becomes inactive.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -505,7 +579,8 @@
 
     // layer3 becomes active and has high refresh rate.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
         summary = summarizeLayerHistory(time);
     }
@@ -536,7 +611,8 @@
 
     // the very first updates makes the layer frequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
         EXPECT_EQ(1, layerCount());
@@ -547,7 +623,8 @@
     }
 
     // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     EXPECT_EQ(1, layerCount());
@@ -559,9 +636,10 @@
     // advance the time for the previous frame to be inactive
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
 
-    // Now event if we post a quick few frame we should stay infrequent
+    // Now even if we post a quick few frame we should stay infrequent
     for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
 
         EXPECT_EQ(1, layerCount());
@@ -572,7 +650,8 @@
     }
 
     // More quick frames will get us to frequent again
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += HI_FPS_PERIOD;
 
     EXPECT_EQ(1, layerCount());
@@ -599,9 +678,10 @@
     nsecs_t time = systemTime();
 
     // Post a buffer to the layers to make them active
-    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    history().record(explicitInvisiblelayer.get(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
+                     time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
+                     time, time, LayerHistory::LayerUpdateType::Buffer);
 
     EXPECT_EQ(2, layerCount());
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -627,7 +707,8 @@
 
     // layer is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -638,7 +719,8 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // another update with the same cadence keep in infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -648,7 +730,8 @@
     EXPECT_EQ(0, animatingLayerCount(time));
 
     // an update as animation will immediately vote for Max
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::AnimationTX);
     time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
 
     ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -658,6 +741,160 @@
     EXPECT_EQ(1, animatingLayerCount(time));
 }
 
+TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // Fill up the window with frequent updates
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
+        time += (60_Hz).getPeriodNsecs();
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // posting a buffer after long inactivity should retain the layer as active
+    time += std::chrono::nanoseconds(3s).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more infrequent buffer should make the layer infrequent
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting another buffer should keep the layer infrequent
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more buffers would mean starting of an animation, so making the layer frequent
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(1, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting a buffer after long inactivity should retain the layer as active
+    time += std::chrono::nanoseconds(3s).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting another buffer should keep the layer frequent
+    time += (60_Hz).getPeriodNsecs();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, inconclusiveLayerBecomingFrequent) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // Fill up the window with frequent updates
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
+        time += (60_Hz).getPeriodNsecs();
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // posting infrequent buffers after long inactivity should make the layer
+    // inconclusive but frequent.
+    time += std::chrono::nanoseconds(3s).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(0, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // posting more buffers should make the layer frequent and switch the refresh rate to max
+    // by clearing the history
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+    EXPECT_EQ(1, clearLayerHistoryCount(time));
+    ASSERT_EQ(1, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+}
+
 TEST_F(LayerHistoryTest, getFramerate) {
     auto layer = createLayer();
 
@@ -673,7 +910,8 @@
 
     // layer is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
 
@@ -741,10 +979,10 @@
     const nsecs_t startTime = systemTime();
 
     const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
-    history().record(heuristicLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
-    history().record(infrequentLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+                     startTime, LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
+                     startTime, LayerHistory::LayerUpdateType::Buffer);
 
     nsecs_t time = startTime;
     nsecs_t lastInfrequentUpdate = startTime;
@@ -752,14 +990,15 @@
     int infrequentLayerUpdates = 0;
     while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
         time += heuristicUpdateDelta.count();
-        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
+                         LayerHistory::LayerUpdateType::Buffer);
 
         if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
             ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
                   totalInfrequentLayerUpdates);
             lastInfrequentUpdate = time;
-            history().record(infrequentLayer.get(), time, time,
-                             LayerHistory::LayerUpdateType::Buffer);
+            history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
+                             time, LayerHistory::LayerUpdateType::Buffer);
             infrequentLayerUpdates++;
         }
 
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
new file mode 100644
index 0000000..97ef5a2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -0,0 +1,392 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "LayerHierarchyTest.h"
+#include "TransactionState.h"
+
+using namespace android::surfaceflinger;
+
+namespace android::surfaceflinger::frontend {
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+    /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+    --gtest_filter="LayerLifecycleManagerTest.*" --gtest_repeat=100 \
+    --gtest_shuffle \
+    --gtest_brief=1
+*/
+class ExpectLayerLifecycleListener : public LayerLifecycleManager::ILifecycleListener {
+public:
+    void onLayerAdded(const RequestedLayerState& layer) override {
+        mActualLayersAdded.push_back(layer.id);
+    };
+    void onLayerDestroyed(const RequestedLayerState& layer) override {
+        mActualLayersDestroyed.emplace(layer.id);
+    };
+
+    void expectLayersAdded(const std::vector<uint32_t>& expectedLayersAdded) {
+        EXPECT_EQ(expectedLayersAdded, mActualLayersAdded);
+        mActualLayersAdded.clear();
+    }
+    void expectLayersDestroyed(const std::unordered_set<uint32_t>& expectedLayersDestroyed) {
+        EXPECT_EQ(expectedLayersDestroyed, mActualLayersDestroyed);
+        mActualLayersDestroyed.clear();
+    }
+
+    std::vector<uint32_t> mActualLayersAdded;
+    std::unordered_set<uint32_t> mActualLayersDestroyed;
+};
+
+class LayerLifecycleManagerTest : public LayerHierarchyTestBase {
+protected:
+    std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) {
+        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true,
+                                                                /*parent=*/UNASSIGNED_LAYER_ID,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
+    }
+
+    std::unique_ptr<RequestedLayerState> childLayer(uint32_t id, uint32_t parentId) {
+        return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/false,
+                                                                parentId,
+                                                                /*mirror=*/UNASSIGNED_LAYER_ID));
+    }
+
+    RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager,
+                                                uint32_t layerId) {
+        return lifecycleManager.getLayerFromId(layerId);
+    }
+};
+
+TEST_F(LayerLifecycleManagerTest, addLayers) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(rootLayer(3));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.onHandlesDestroyed({1, 2, 3});
+    EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    listener->expectLayersAdded({1, 2, 3});
+    listener->expectLayersDestroyed({1, 2, 3});
+}
+
+TEST_F(LayerLifecycleManagerTest, updateLayerStates) {
+    LayerLifecycleManager lifecycleManager;
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.applyTransactions(setZTransaction(1, 2));
+
+    auto& managedLayers = lifecycleManager.getLayers();
+    ASSERT_EQ(managedLayers.size(), 1u);
+
+    EXPECT_EQ(managedLayers.front()->z, 2);
+    EXPECT_TRUE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+    EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    ASSERT_EQ(managedLayers.size(), 1u);
+    EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z));
+
+    // apply transactions that do not affect the hierarchy
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.backgroundBlurRadius = 22;
+    transactions.back().states.front().state.what = layer_state_t::eBackgroundBlurRadiusChanged;
+    transactions.back().states.front().layerId = 1;
+    lifecycleManager.applyTransactions(transactions);
+    EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    EXPECT_EQ(managedLayers.front()->backgroundBlurRadius, 22u);
+}
+
+TEST_F(LayerLifecycleManagerTest, layerWithoutHandleIsDestroyed) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.onHandlesDestroyed({1});
+    lifecycleManager.commitChanges();
+
+    SCOPED_TRACE("layerWithoutHandleIsDestroyed");
+    listener->expectLayersAdded({1, 2});
+    listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, rootLayerWithoutHandleIsDestroyed) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.onHandlesDestroyed({1});
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2});
+    listener->expectLayersDestroyed({1});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.onHandlesDestroyed({3});
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    layers.emplace_back(childLayer(4, /*parent*/ 3));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3, 4});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
+    lifecycleManager.onHandlesDestroyed({3});
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({3});
+}
+
+TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    layers.emplace_back(childLayer(4, /*parent*/ 3));
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3, 4});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
+    lifecycleManager.onHandlesDestroyed({3, 4});
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({3, 4});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3, 4});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
+    EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+    lifecycleManager.applyTransactions(reparentLayerTransaction(4, 2));
+    EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3, 4});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
+    EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+    lifecycleManager.applyTransactions(reparentLayerTransaction(4, UNASSIGNED_LAYER_ID));
+    EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    layers.emplace_back(rootLayer(2));
+    layers.emplace_back(childLayer(3, /*parent*/ 2));
+    layers.emplace_back(childLayer(4, /*parent*/ 3));
+
+    lifecycleManager.addLayers(std::move(layers));
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({1, 2, 3, 4});
+    listener->expectLayersDestroyed({});
+
+    lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1));
+    EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+    lifecycleManager.applyTransactions(setZTransaction(4, 1));
+    EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf);
+
+    lifecycleManager.commitChanges();
+    listener->expectLayersAdded({});
+    listener->expectLayersDestroyed({});
+}
+
+TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    lifecycleManager.addLayers(std::move(layers));
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.bgColor.a = 0.5;
+    transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+    transactions.back().states.front().layerId = 1;
+    lifecycleManager.applyTransactions(transactions);
+
+    auto& managedLayers = lifecycleManager.getLayers();
+    ASSERT_EQ(managedLayers.size(), 2u);
+
+    EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
+    listener->expectLayersAdded({1, bgLayerId});
+    listener->expectLayersDestroyed({});
+    EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf);
+}
+
+TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    lifecycleManager.addLayers(std::move(layers));
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.bgColor.a = 0.5;
+    transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+    transactions.back().states.front().layerId = 1;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.bgColor.a = 0;
+    transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+    transactions.back().states.front().layerId = 1;
+
+    lifecycleManager.applyTransactions(transactions);
+
+    ASSERT_EQ(lifecycleManager.getLayers().size(), 1u);
+    ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 1u);
+
+    EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
+    listener->expectLayersAdded({1, bgLayerId});
+    listener->expectLayersDestroyed({bgLayerId});
+}
+
+TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) {
+    LayerLifecycleManager lifecycleManager;
+    auto listener = std::make_shared<ExpectLayerLifecycleListener>();
+    lifecycleManager.addLifecycleListener(listener);
+
+    std::vector<std::unique_ptr<RequestedLayerState>> layers;
+    layers.emplace_back(rootLayer(1));
+    lifecycleManager.addLayers(std::move(layers));
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.bgColor.a = 0.5;
+    transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+    transactions.back().states.front().layerId = 1;
+    transactions.emplace_back();
+    lifecycleManager.applyTransactions(transactions);
+    lifecycleManager.onHandlesDestroyed({1});
+
+    ASSERT_EQ(lifecycleManager.getLayers().size(), 0u);
+    ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u);
+
+    EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+    lifecycleManager.commitChanges();
+    ASSERT_EQ(listener->mActualLayersAdded.size(), 2u);
+    auto bgLayerId = listener->mActualLayersAdded[1];
+    listener->expectLayersAdded({1, bgLayerId});
+    listener->expectLayersDestroyed({1, bgLayerId});
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
index 373fd74..e6e02c1 100644
--- a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp
@@ -27,6 +27,8 @@
 #include <gui/LayerMetadata.h>
 #include <log/log.h>
 
+using android::gui::LayerMetadata;
+
 namespace android {
 namespace {
 
@@ -113,4 +115,4 @@
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
new file mode 100644
index 0000000..b8a7446
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -0,0 +1,465 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
+#include "LayerHierarchyTest.h"
+
+#define UPDATE_AND_VERIFY(BUILDER, ...)                                    \
+    ({                                                                     \
+        SCOPED_TRACE("");                                                  \
+        updateAndVerify((BUILDER), /*displayChanges=*/false, __VA_ARGS__); \
+    })
+
+#define UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(BUILDER, ...)              \
+    ({                                                                    \
+        SCOPED_TRACE("");                                                 \
+        updateAndVerify((BUILDER), /*displayChanges=*/true, __VA_ARGS__); \
+    })
+
+namespace android::surfaceflinger::frontend {
+
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+    /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+    --gtest_filter="LayerSnapshotTest.*" --gtest_brief=1
+*/
+
+class LayerSnapshotTest : public LayerHierarchyTestBase {
+protected:
+    LayerSnapshotTest() : LayerHierarchyTestBase() {
+        UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    }
+
+    void createRootLayer(uint32_t id) override {
+        LayerHierarchyTestBase::createRootLayer(id);
+        setColor(id);
+    }
+
+    void createLayer(uint32_t id, uint32_t parentId) override {
+        LayerHierarchyTestBase::createLayer(id, parentId);
+        setColor(parentId);
+    }
+
+    void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
+        LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
+        setColor(id);
+    }
+
+    void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges,
+                         const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) {
+        if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+            mHierarchyBuilder.update(mLifecycleManager.getLayers(),
+                                     mLifecycleManager.getDestroyedLayers());
+        }
+        LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+                                        .layerLifecycleManager = mLifecycleManager,
+                                        .includeMetadata = false,
+                                        .displays = mFrontEndDisplayInfos,
+                                        .displayChanges = hasDisplayChanges,
+                                        .globalShadowSettings = globalShadowSettings,
+                                        .supportsBlur = true,
+                                        .supportedLayerGenericMetadata = {},
+                                        .genericLayerMetadataKeyMap = {}};
+        actualBuilder.update(args);
+
+        // rebuild layer snapshots from scratch and verify that it matches the updated state.
+        LayerSnapshotBuilder expectedBuilder(args);
+        mLifecycleManager.commitChanges();
+        ASSERT_TRUE(expectedBuilder.getSnapshots().size() > 0);
+        ASSERT_TRUE(actualBuilder.getSnapshots().size() > 0);
+
+        std::vector<uint32_t> actualVisibleLayerIdsInZOrder;
+        actualBuilder.forEachVisibleSnapshot(
+                [&actualVisibleLayerIdsInZOrder](const LayerSnapshot& snapshot) {
+                    actualVisibleLayerIdsInZOrder.push_back(snapshot.path.id);
+                });
+        EXPECT_EQ(expectedVisibleLayerIdsInZOrder, actualVisibleLayerIdsInZOrder);
+    }
+
+    LayerSnapshot* getSnapshot(uint32_t layerId) { return mSnapshotBuilder.getSnapshot(layerId); }
+    LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
+        return mSnapshotBuilder.getSnapshot(path);
+    }
+
+    LayerHierarchyBuilder mHierarchyBuilder{{}};
+    LayerSnapshotBuilder mSnapshotBuilder;
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
+    renderengine::ShadowSettings globalShadowSettings;
+    static const std::vector<uint32_t> STARTING_ZORDER;
+};
+const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1,   11,   111, 12, 121,
+                                                                  122, 1221, 13,  2};
+
+TEST_F(LayerSnapshotTest, buildSnapshot) {
+    LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+                                    .layerLifecycleManager = mLifecycleManager,
+                                    .includeMetadata = false,
+                                    .displays = mFrontEndDisplayInfos,
+                                    .globalShadowSettings = globalShadowSettings,
+                                    .supportedLayerGenericMetadata = {},
+                                    .genericLayerMetadataKeyMap = {}};
+    LayerSnapshotBuilder builder(args);
+}
+
+TEST_F(LayerSnapshotTest, updateSnapshot) {
+    LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+                                    .layerLifecycleManager = mLifecycleManager,
+                                    .includeMetadata = false,
+                                    .displays = mFrontEndDisplayInfos,
+                                    .globalShadowSettings = globalShadowSettings,
+                                    .supportedLayerGenericMetadata = {},
+                                    .genericLayerMetadataKeyMap = {}
+
+    };
+
+    LayerSnapshotBuilder builder;
+    builder.update(args);
+}
+
+// update using parent snapshot data
+TEST_F(LayerSnapshotTest, croppedByParent) {
+    /// MAKE ALL LAYERS VISIBLE BY DEFAULT
+    DisplayInfo info;
+    info.info.logicalHeight = 100;
+    info.info.logicalWidth = 200;
+    mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+    Rect 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());
+    float maxHeight = static_cast<float>(info.info.logicalHeight * 10);
+    float maxWidth = static_cast<float>(info.info.logicalWidth * 10);
+
+    FloatRect maxDisplaySize(-maxWidth, -maxHeight, maxWidth, maxHeight);
+    EXPECT_EQ(getSnapshot(1)->geomLayerBounds, maxDisplaySize);
+}
+
+// visibility tests
+TEST_F(LayerSnapshotTest, newLayerHiddenByPolicy) {
+    createLayer(112, 11);
+    hideLayer(112);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    showLayer(112);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 111, 112, 12, 121, 122, 1221, 13, 2});
+}
+
+TEST_F(LayerSnapshotTest, hiddenByParent) {
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+}
+
+TEST_F(LayerSnapshotTest, reparentShowsChild) {
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+    showLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+}
+
+TEST_F(LayerSnapshotTest, reparentHidesChild) {
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+    reparentLayer(121, 11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 122, 1221, 13, 2});
+}
+
+TEST_F(LayerSnapshotTest, unHidingUpdatesSnapshot) {
+    hideLayer(11);
+    Rect crop(1, 2, 3, 4);
+    setCrop(111, crop);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+    showLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(111)->geomLayerBounds, crop.toFloatRect());
+}
+
+TEST_F(LayerSnapshotTest, childBehindParentCanBeHiddenByParent) {
+    setZ(111, -1);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 111, 11, 12, 121, 122, 1221, 13, 2});
+
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+}
+
+// relative tests
+TEST_F(LayerSnapshotTest, RelativeParentCanHideChild) {
+    reparentRelativeLayer(13, 11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2});
+
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 2});
+}
+
+TEST_F(LayerSnapshotTest, ReparentingToHiddenRelativeParentHidesChild) {
+    hideLayer(11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+    reparentRelativeLayer(13, 11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 2});
+}
+
+TEST_F(LayerSnapshotTest, AlphaInheritedByChildren) {
+    setAlpha(1, 0.5);
+    setAlpha(122, 0.5);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(12)->alpha, 0.5f);
+    EXPECT_EQ(getSnapshot(1221)->alpha, 0.25f);
+}
+
+// Change states
+TEST_F(LayerSnapshotTest, UpdateClearsPreviousChangeStates) {
+    setCrop(1, Rect(1, 2, 3, 4));
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(1)->changes.get() != 0);
+    EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
+    setCrop(2, Rect(1, 2, 3, 4));
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(2)->changes.get() != 0);
+    EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
+    EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+}
+
+TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) {
+    setColor(11, {1._hf, 0._hf, 0._hf});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(11)->changes.get() != 0);
+    EXPECT_TRUE(getSnapshot(1)->changes.get() == 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_TRUE(getSnapshot(11)->changes.get() == 0);
+}
+
+TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) {
+    setColor(1, {1._hf, 0._hf, 0._hf});
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
+}
+
+TEST_F(LayerSnapshotTest, GameMode) {
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+    transactions.back().states.front().state.metadata = LayerMetadata();
+    transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
+    transactions.back().states.front().layerId = 1;
+    transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+    mLifecycleManager.applyTransactions(transactions);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
+    EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
+}
+
+TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) {
+    // ROOT
+    // ├── 1
+    // │   ├── 11 (frame rate set)
+    // │   │   └── 111
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   └── 122
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+
+    std::vector<TransactionState> transactions;
+    transactions.emplace_back();
+    transactions.back().states.push_back({});
+    transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+    transactions.back().states.front().state.frameRate = 90.0;
+    transactions.back().states.front().state.frameRateCompatibility =
+            ANATIVEWINDOW_FRAME_RATE_EXACT;
+    transactions.back().states.front().state.changeFrameRateStrategy =
+            ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
+    transactions.back().states.front().layerId = 11;
+    mLifecycleManager.applyTransactions(transactions);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_EQ(getSnapshot(11)->frameRate.rate.getIntValue(), 90);
+    EXPECT_EQ(getSnapshot(11)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::Exact);
+    EXPECT_EQ(getSnapshot(111)->frameRate.rate.getIntValue(), 90);
+    EXPECT_EQ(getSnapshot(111)->frameRate.type,
+              scheduler::LayerInfo::FrameRateCompatibility::Exact);
+    EXPECT_EQ(getSnapshot(1)->frameRate.rate.getIntValue(), 0);
+    EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+}
+
+TEST_F(LayerSnapshotTest, canCropTouchableRegion) {
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111 (touchregion set to touch but cropped by layer 13)
+    // │   ├── 12
+    // │   │   ├── 121
+    // │   │   └── 122
+    // │   │       └── 1221
+    // │   └── 13 (crop set to touchCrop)
+    // └── 2
+
+    Rect touchCrop{300, 300, 400, 500};
+    setCrop(13, touchCrop);
+    Region touch{Rect{0, 0, 1000, 1000}};
+    setTouchableRegionCrop(111, touch, /*touchCropId=*/13, /*replaceTouchableRegionWithCrop=*/true);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), touchCrop);
+
+    Rect modifiedTouchCrop{100, 300, 400, 700};
+    setCrop(13, modifiedTouchCrop);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop);
+}
+
+TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) {
+    static constexpr int blurRadius = 42;
+    setBackgroundBlurRadius(1221, blurRadius);
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius);
+
+    static constexpr float alpha = 0.5;
+    setAlpha(12, alpha);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius * alpha);
+}
+
+// Display Mirroring Tests
+// tree with 3 levels of children
+// ROOT (DISPLAY 0)
+// ├── 1
+// │   ├── 11
+// │   │   └── 111
+// │   ├── 12 (has skip screenshot flag)
+// │   │   ├── 121
+// │   │   └── 122
+// │   │       └── 1221
+// │   └── 13
+// └── 2
+// ROOT (DISPLAY 1)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, displayMirrorRespectsLayerSkipScreenshotFlag) {
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3, 1, 11, 111, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+}
+
+// ROOT (DISPLAY 0)
+// ├── 1
+// │   ├── 11
+// │   │   └── 111
+// │   └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, mirrorLayerGetsCorrectLayerStack) {
+    reparentLayer(12, UNASSIGNED_LAYER_ID);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 3);
+    createDisplayMirrorLayer(4, ui::LayerStack::fromValue(0));
+    setLayerStack(4, 4);
+
+    std::vector<uint32_t> expected = {1,  11, 111, 13, 2,  3,   1,  11, 111,
+                                      13, 2,  4,   1,  11, 111, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+    EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 3})->outputFilter.layerStack.id, 3u);
+    EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 4})->outputFilter.layerStack.id, 4u);
+}
+
+// ROOT (DISPLAY 0)
+// ├── 1 (crop 50x50)
+// │   ├── 11
+// │   │   └── 111
+// │   └── 13
+// └── 2
+// ROOT (DISPLAY 3)
+// └── 3 (mirrors display 0) (crop 100x100)
+TEST_F(LayerSnapshotTest, mirrorLayerTouchIsCroppedByMirrorRoot) {
+    reparentLayer(12, UNASSIGNED_LAYER_ID);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 3);
+    setCrop(1, Rect{50, 50});
+    setCrop(3, Rect{100, 100});
+    setCrop(111, Rect{200, 200});
+    Region touch{Rect{0, 0, 1000, 1000}};
+    setTouchableRegion(111, touch);
+    std::vector<uint32_t> expected = {1, 11, 111, 13, 2, 3, 1, 11, 111, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+    EXPECT_TRUE(getSnapshot({.id = 111})->inputInfo.touchableRegion.hasSameRects(touch));
+    Region touchCroppedByMirrorRoot{Rect{0, 0, 50, 50}};
+    EXPECT_TRUE(getSnapshot({.id = 111, .mirrorRootId = 3})
+                        ->inputInfo.touchableRegion.hasSameRects(touchCroppedByMirrorRoot));
+}
+
+TEST_F(LayerSnapshotTest, canRemoveDisplayMirror) {
+    setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3, 1, 11, 111, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+    destroyLayerHandle(3);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterMirroring) {
+    size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+    createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+    setLayerStack(3, 1);
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3,
+                                      1, 11, 111, 12, 121, 122, 1221, 13, 2};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+    destroyLayerHandle(3);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+// Rel z doesn't create duplicate snapshots but this is for completeness
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterRelZ) {
+    size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+    reparentRelativeLayer(13, 11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2});
+    setZ(13, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size());
+}
+
+TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterLayerDestruction) {
+    size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size();
+    destroyLayerHandle(2);
+    destroyLayerHandle(122);
+
+    std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+    EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size());
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
index 4974f90..95e54f6 100644
--- a/services/surfaceflinger/tests/unittests/LayerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTest.cpp
@@ -17,7 +17,6 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
-#include <EffectLayer.h>
 #include <gtest/gtest.h>
 #include <ui/FloatRect.h>
 #include <ui/Transform.h>
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
index 5a2c147..803e807 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -16,26 +16,19 @@
 
 #include "LayerTestUtils.h"
 
-#include "mock/MockEventThread.h"
-
 namespace android {
 
-using testing::_;
-using testing::Return;
-
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
 sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
                            LayerMetadata());
-    return new BufferStateLayer(args);
+    return sp<Layer>::make(args);
 }
 
 sp<Layer> EffectLayerFactory::createLayer(TestableSurfaceFlinger& flinger) {
     sp<Client> client;
     LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return new EffectLayer(args);
+    return sp<Layer>::make(args);
 }
 
 std::string PrintToStringParamName(
@@ -44,34 +37,7 @@
 }
 
 BaseLayerTest::BaseLayerTest() {
-    setupScheduler();
-}
-
-void BaseLayerTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread),
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            TestableSurfaceFlinger::kTwoDisplayModes);
+    mFlinger.setupMockScheduler();
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
index fc9b6a2..0773d90 100644
--- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h
+++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -23,8 +23,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
@@ -65,8 +63,6 @@
 protected:
     BaseLayerTest();
 
-    void setupScheduler();
-
     TestableSurfaceFlinger mFlinger;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index e0aa0b1..8f1b450 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -23,6 +23,7 @@
 #include "FrameTimeline.h"
 #include "Scheduler/MessageQueue.h"
 #include "SurfaceFlinger.h"
+#include "mock/MockVSyncDispatch.h"
 
 namespace android {
 
@@ -32,8 +33,9 @@
 using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
 
 struct NoOpCompositor final : ICompositor {
-    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t, int64_t) override {}
+    void configure() override {}
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 } gNoOpCompositor;
 
@@ -41,12 +43,15 @@
     struct MockHandler : MessageQueue::Handler {
         using MessageQueue::Handler::Handler;
 
-        MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override));
+        MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override));
     };
 
     explicit TestableMessageQueue(sp<MockHandler> handler)
           : impl::MessageQueue(gNoOpCompositor, handler), mHandler(std::move(handler)) {}
 
+    // impl::MessageQueue overrides:
+    void onFrameSignal(ICompositor&, VsyncId, TimePoint) override {}
+
 public:
     TestableMessageQueue() : TestableMessageQueue(sp<MockHandler>::make(*this)) {}
 
@@ -55,14 +60,6 @@
     const sp<MockHandler> mHandler;
 };
 
-struct MockVSyncDispatch : scheduler::VSyncDispatch {
-    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
-    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-};
-
 struct MockTokenManager : frametimeline::TokenManager {
     MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
     MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
@@ -70,31 +67,30 @@
 
 struct MessageQueueTest : testing::Test {
     void SetUp() override {
-        EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
-        EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
-        EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+        EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
+        EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration));
+        EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
     }
 
-    MockVSyncDispatch mVSyncDispatch;
+    std::shared_ptr<mock::VSyncDispatch> mVSyncDispatch = std::make_shared<mock::VSyncDispatch>();
     MockTokenManager mTokenManager;
     TestableMessageQueue mEventQueue;
 
     const CallbackToken mCallbackToken{5};
-    constexpr static auto mDuration = std::chrono::nanoseconds(100ms);
-    constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms);
+
+    static constexpr Duration kDuration = 100ms;
+    static constexpr Duration kDifferentDuration = 250ms;
 };
 
 namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
 TEST_F(MessageQueueTest, commit) {
-    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
     EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
     ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
@@ -103,17 +99,17 @@
 
 TEST_F(MessageQueueTest, commitTwice) {
     InSequence s;
-    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
     ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
     EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
     ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
@@ -122,47 +118,50 @@
 
 TEST_F(MessageQueueTest, commitTwiceWithCallback) {
     InSequence s;
-    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+    const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
     ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
     EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
 
-    const auto startTime = 100;
-    const auto endTime = startTime + mDuration.count();
-    const auto presentTime = 500;
-    const auto vsyncId = 42;
+    constexpr TimePoint kStartTime = TimePoint::fromNs(100);
+    constexpr TimePoint kEndTime = kStartTime + kDuration;
+    constexpr TimePoint kPresentTime = TimePoint::fromNs(500);
+    constexpr VsyncId vsyncId{42};
+
     EXPECT_CALL(mTokenManager,
-                generateTokenForPredictions(
-                        frametimeline::TimelineItem(startTime, endTime, presentTime)))
-            .WillOnce(Return(vsyncId));
-    EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1);
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime));
+                generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(),
+                                                                        kEndTime.ns(),
+                                                                        kPresentTime.ns())))
+            .WillOnce(Return(vsyncId.value));
+    EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1);
+    EXPECT_NO_FATAL_FAILURE(
+            mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns()));
 
     EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
 
     const auto timingAfterCallback =
-            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                      .readyDuration = 0,
-                                                     .earliestVsync = presentTime};
+                                                     .earliestVsync = kPresentTime.ns()};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
 TEST_F(MessageQueueTest, commitWithDurationChange) {
-    EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
+    EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(kDifferentDuration));
 
     const auto timing =
-            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(),
+            scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDifferentDuration.ns(),
                                                      .readyDuration = 0,
                                                      .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index aafc323..d08e12c 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -224,7 +224,8 @@
     EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
 }
 
-TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_noCallbacksAfterStopAndResetTest) {
     fake::FakeClock* clock = new fake::FakeClock();
     mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
                                                            mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 8711a42..0d66d59 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -25,13 +25,15 @@
 #include <ui/DisplayId.h>
 #include <chrono>
 #include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"
+#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "mock/DisplayHardware/MockPowerHalController.h"
 
 using namespace android;
 using namespace android::Hwc2::mock;
 using namespace android::hardware::power;
 using namespace std::chrono_literals;
 using namespace testing;
+using namespace android::power;
 
 namespace android::Hwc2::impl {
 
@@ -39,49 +41,57 @@
 public:
     void SetUp() override;
     void startPowerHintSession();
-    void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
-    void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
-    nsecs_t getFenceWaitDelayDuration(bool skipValidate);
+    void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
+    void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
+    Duration getFenceWaitDelayDuration(bool skipValidate);
+    Duration getErrorMargin();
 
 protected:
     TestableSurfaceFlinger mFlinger;
     std::unique_ptr<PowerAdvisor> mPowerAdvisor;
-    NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
-    nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count();
+    MockPowerHalController* mMockPowerHalController;
+    sp<MockIPowerHintSession> mMockPowerHintSession;
 };
 
-void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
-    std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
-            std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
-    mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
-    ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
-    ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
-    mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
-    mMockAidlWrapper =
-            reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
+void PowerAdvisorTest::SetUp() {
+    mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
+    mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
+    mMockPowerHalController =
+            reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
+    ON_CALL(*mMockPowerHalController, getHintSessionPreferredRate)
+            .WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
 }
 
 void PowerAdvisorTest::startPowerHintSession() {
     const std::vector<int32_t> threadIds = {1, 2, 3};
-    mPowerAdvisor->enablePowerHint(true);
+    mMockPowerHintSession = android::sp<NiceMock<MockIPowerHintSession>>::make();
+    ON_CALL(*mMockPowerHalController, createHintSession)
+            .WillByDefault(
+                    Return(HalResult<sp<IPowerHintSession>>::fromStatus(binder::Status::ok(),
+                                                                        mMockPowerHintSession)));
+    mPowerAdvisor->enablePowerHintSession(true);
     mPowerAdvisor->startPowerHintSession(threadIds);
 }
 
-void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) {
-    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget);
+void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
+                                         TimePoint expectedPresentTime) {
+    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTargetDuration);
     mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
 }
 
-void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) {
+void PowerAdvisorTest::fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod) {
     mPowerAdvisor->setCommitStart(startTime);
-    mPowerAdvisor->setFrameDelay(0);
-    mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+    mPowerAdvisor->setFrameDelay(0ns);
+    mPowerAdvisor->updateTargetWorkDuration(vsyncPeriod);
 }
 
-nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
+Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
     return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
-                         : PowerAdvisor::kFenceWaitStartDelayValidated)
-            .count();
+                         : PowerAdvisor::kFenceWaitStartDelayValidated);
+}
+
+Duration PowerAdvisorTest::getErrorMargin() {
+    return mPowerAdvisor->sTargetSafetyMargin;
 }
 
 namespace {
@@ -93,11 +103,11 @@
     std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
 
     // 60hz
-    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
-    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
-    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+    const Duration presentDuration = 5ms;
+    const Duration postCompDuration = 1ms;
 
-    nsecs_t startTime = 100;
+    TimePoint startTime{100ns};
 
     // advisor only starts on frame 2 so do an initial no-op frame
     fakeBasicFrameTiming(startTime, vsyncPeriod);
@@ -109,16 +119,19 @@
     // increment the frame
     startTime += vsyncPeriod;
 
-    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
-    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+    const Duration expectedDuration = getErrorMargin() + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockPowerHintSession,
+                reportActualWorkDuration(ElementsAre(
+                        Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
+            .Times(1);
 
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
     mPowerAdvisor->setDisplays(displayIds);
-    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
-    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
     mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
-    mPowerAdvisor->sendActualWorkDuration();
+    mPowerAdvisor->reportActualWorkDuration();
 }
 
 TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
@@ -128,12 +141,12 @@
     std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};
 
     // 60hz
-    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
-    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
-    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
-    const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count();
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
+    const Duration presentDuration = 5ms;
+    const Duration postCompDuration = 1ms;
+    const Duration hwcBlockedDuration = 500us;
 
-    nsecs_t startTime = 100;
+    TimePoint startTime{100ns};
 
     // advisor only starts on frame 2 so do an initial no-op frame
     fakeBasicFrameTiming(startTime, vsyncPeriod);
@@ -145,19 +158,22 @@
     // increment the frame
     startTime += vsyncPeriod;
 
-    const nsecs_t expectedDuration = kErrorMargin + presentDuration +
+    const Duration expectedDuration = getErrorMargin() + presentDuration +
             getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
-    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+    EXPECT_CALL(*mMockPowerHintSession,
+                reportActualWorkDuration(ElementsAre(
+                        Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
+            .Times(1);
 
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
     mPowerAdvisor->setDisplays(displayIds);
-    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
-    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 3ms);
     // now report the fence as having fired during the display HWC time
-    mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration,
+    mPowerAdvisor->setSfPresentTiming(startTime + 2ms + hwcBlockedDuration,
                                       startTime + presentDuration);
-    mPowerAdvisor->sendActualWorkDuration();
+    mPowerAdvisor->reportActualWorkDuration();
 }
 
 TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
@@ -168,12 +184,12 @@
                                       GpuVirtualDisplayId(1)};
 
     // 60hz
-    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
+    const Duration vsyncPeriod{std::chrono::nanoseconds(1s) / 60};
     // make present duration much later than the hwc display by itself will account for
-    const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count();
-    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
+    const Duration presentDuration{10ms};
+    const Duration postCompDuration{1ms};
 
-    nsecs_t startTime = 100;
+    TimePoint startTime{100ns};
 
     // advisor only starts on frame 2 so do an initial no-op frame
     fakeBasicFrameTiming(startTime, vsyncPeriod);
@@ -185,18 +201,21 @@
     // increment the frame
     startTime += vsyncPeriod;
 
-    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
-    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);
+    const Duration expectedDuration = getErrorMargin() + presentDuration + postCompDuration;
+    EXPECT_CALL(*mMockPowerHintSession,
+                reportActualWorkDuration(ElementsAre(
+                        Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
+            .Times(1);
 
     fakeBasicFrameTiming(startTime, vsyncPeriod);
     setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
     mPowerAdvisor->setDisplays(displayIds);
 
     // don't report timing for the gpu displays since they don't use hwc
-    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
-    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
+    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1ms, startTime + 1500us);
+    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2ms, startTime + 2500us);
     mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
-    mPowerAdvisor->sendActualWorkDuration();
+    mPowerAdvisor->reportActualWorkDuration();
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
deleted file mode 100644
index 188fd58..0000000
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ /dev/null
@@ -1,2077 +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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SchedulerUnittests"
-
-#include <ftl/enum.h>
-#include <gmock/gmock.h>
-#include <log/log.h>
-#include <ui/Size.h>
-
-#include "DisplayHardware/HWC2.h"
-#include "FpsOps.h"
-#include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplayMode.h"
-
-using namespace std::chrono_literals;
-
-namespace android::scheduler {
-
-namespace hal = android::hardware::graphics::composer::hal;
-
-using LayerVoteType = RefreshRateConfigs::LayerVoteType;
-using LayerRequirement = RefreshRateConfigs::LayerRequirement;
-
-using mock::createDisplayMode;
-
-struct TestableRefreshRateConfigs : RefreshRateConfigs {
-    using RefreshRateConfigs::RefreshRateConfigs;
-
-    DisplayModePtr getMinSupportedRefreshRate() const {
-        std::lock_guard lock(mLock);
-        return mMinRefreshRateModeIt->second;
-    }
-
-    DisplayModePtr getMaxSupportedRefreshRate() const {
-        std::lock_guard lock(mLock);
-        return mMaxRefreshRateModeIt->second;
-    }
-
-    DisplayModePtr getMinRefreshRateByPolicy() const {
-        std::lock_guard lock(mLock);
-        return getMinRefreshRateByPolicyLocked();
-    }
-
-    const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
-
-    using RefreshRateConfigs::GetBestRefreshRateCache;
-    auto& mutableGetBestRefreshRateCache() { return mGetBestRefreshRateCache; }
-
-    auto getBestRefreshRateAndSignals(const std::vector<LayerRequirement>& layers,
-                                      GlobalSignals signals) const {
-        return RefreshRateConfigs::getBestRefreshRate(layers, signals);
-    }
-
-    DisplayModePtr getBestRefreshRate(const std::vector<LayerRequirement>& layers = {},
-                                      GlobalSignals signals = {}) const {
-        return getBestRefreshRateAndSignals(layers, signals).first;
-    }
-};
-
-class RefreshRateConfigsTest : public testing::Test {
-protected:
-    RefreshRateConfigsTest();
-    ~RefreshRateConfigsTest();
-
-    static constexpr DisplayModeId kModeId60{0};
-    static constexpr DisplayModeId kModeId90{1};
-    static constexpr DisplayModeId kModeId72{2};
-    static constexpr DisplayModeId kModeId120{3};
-    static constexpr DisplayModeId kModeId30{4};
-    static constexpr DisplayModeId kModeId25{5};
-    static constexpr DisplayModeId kModeId50{6};
-    static constexpr DisplayModeId kModeId24{7};
-    static constexpr DisplayModeId kModeId24Frac{8};
-    static constexpr DisplayModeId kModeId30Frac{9};
-    static constexpr DisplayModeId kModeId60Frac{10};
-
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
-    static inline const DisplayModePtr kMode90_4K =
-            createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
-    static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
-    static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
-    static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
-    static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
-    static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
-    static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
-    static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
-    static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
-    static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
-    static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
-    static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
-
-    // Test configurations.
-    static inline const DisplayModes kModes_60 = makeModes(kMode60);
-    static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
-    static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
-    static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
-    static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72);
-    static inline const DisplayModes kModes_60_90_72_120 =
-            makeModes(kMode60, kMode90, kMode72, kMode120);
-    static inline const DisplayModes kModes_30_60_72_90_120 =
-            makeModes(kMode60, kMode90, kMode72, kMode120, kMode30);
-
-    static inline const DisplayModes kModes_30_60 =
-            makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30);
-    static inline const DisplayModes kModes_30_60_72_90 =
-            makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30);
-    static inline const DisplayModes kModes_30_60_90 =
-            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30);
-    static inline const DisplayModes kModes_25_30_50_60 =
-            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
-    static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
-
-    // This is a typical TV configuration.
-    static inline const DisplayModes kModes_24_25_30_50_60_Frac =
-            makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
-                      kMode60Frac);
-};
-
-RefreshRateConfigsTest::RefreshRateConfigsTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-RefreshRateConfigsTest::~RefreshRateConfigsTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-namespace {
-
-TEST_F(RefreshRateConfigsTest, oneMode_canSwitch) {
-    RefreshRateConfigs configs(kModes_60, kModeId60);
-    EXPECT_FALSE(configs.canSwitch());
-}
-
-TEST_F(RefreshRateConfigsTest, invalidPolicy) {
-    RefreshRateConfigs configs(kModes_60, kModeId60);
-    EXPECT_LT(configs.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
-    EXPECT_LT(configs.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}), 0);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    const auto minRate = configs.getMinSupportedRefreshRate();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
-
-    EXPECT_EQ(kMode60, minRate);
-    EXPECT_EQ(kMode90, performanceRate);
-
-    const auto minRateByPolicy = configs.getMinRefreshRateByPolicy();
-    const auto performanceRateByPolicy = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(minRateByPolicy, minRate);
-    EXPECT_EQ(performanceRateByPolicy, performanceRate);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentGroups) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode60, minRate);
-    EXPECT_EQ(kMode60, minRate60);
-    EXPECT_EQ(kMode60, performanceRate60);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
-    configs.setActiveModeId(kModeId90);
-
-    const auto minRate90 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode90_G1, performanceRate);
-    EXPECT_EQ(kMode90_G1, minRate90);
-    EXPECT_EQ(kMode90_G1, performanceRate90);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
-    TestableRefreshRateConfigs configs(kModes_60_90_4K, kModeId60);
-
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxSupportedRefreshRate();
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode60, minRate);
-    EXPECT_EQ(kMode60, minRate60);
-    EXPECT_EQ(kMode60, performanceRate60);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}), 0);
-    configs.setActiveModeId(kModeId90);
-
-    const auto minRate90 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate90 = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode90_4K, performanceRate);
-    EXPECT_EQ(kMode90_4K, minRate90);
-    EXPECT_EQ(kMode90_4K, performanceRate90);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_policyChange) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    const auto minRate = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode60, minRate);
-    EXPECT_EQ(kMode90, performanceRate);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-
-    const auto minRate60 = configs.getMinRefreshRateByPolicy();
-    const auto performanceRate60 = configs.getMaxRefreshRateByPolicy();
-
-    EXPECT_EQ(kMode60, minRate60);
-    EXPECT_EQ(kMode60, performanceRate60);
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_getActiveMode) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-    {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId60);
-    }
-
-    configs.setActiveModeId(kModeId90);
-    {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
-    }
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
-    {
-        const auto mode = configs.getActiveMode();
-        EXPECT_EQ(mode->getId(), kModeId90);
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
-    {
-        TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId72);
-
-        // If there are no layers we select the default frame rate, which is the max of the primary
-        // range.
-        EXPECT_EQ(kMode90, configs.getBestRefreshRate());
-
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), NO_ERROR);
-        EXPECT_EQ(kMode60, configs.getBestRefreshRate());
-    }
-    {
-        // We select max even when this will cause a non-seamless switch.
-        TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-        constexpr bool kAllowGroupSwitching = true;
-        EXPECT_EQ(configs.setDisplayManagerPolicy({kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}),
-                  NO_ERROR);
-        EXPECT_EQ(kMode90_G1, configs.getBestRefreshRate());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::Min;
-    lr.name = "Min";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.name = "";
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-
-    lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
-
-    lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}), 0);
-    lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::Min;
-    lr.name = "Min";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
-    TestableRefreshRateConfigs configs(kModes_60_72_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 48_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 48_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.name = "24Hz Heuristic";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
-                                       {.frameRateMultipleThreshold = 120});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-    auto& lr3 = layers[2];
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "60Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.name = "24Hz Heuristic";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.name = "24Hz ExplicitDefault";
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.name = "90Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Max;
-    lr2.name = "Max";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 120_Hz;
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.name = "120Hz ExplicitDefault";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 24_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "24Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 120_Hz;
-    lr2.vote = LayerVoteType::ExplicitExact;
-    lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 10_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "30Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 120_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "120Hz ExplicitExact";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    lr1.desiredRefreshRate = 30_Hz;
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.name = "30Hz ExplicitExactOrMultiple";
-    lr2.desiredRefreshRate = 30_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.name = "30Hz ExplicitExactOrMultiple";
-    lr3.vote = LayerVoteType::Heuristic;
-    lr3.desiredRefreshRate = 120_Hz;
-    lr3.name = "120Hz Heuristic";
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
-    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::Min;
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 30_Hz;
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::Min;
-    lr.name = "Min";
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    lr.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 90_Hz;
-    lr.vote = LayerVoteType::Heuristic;
-    lr.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Heuristic";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr.desiredRefreshRate = 45_Hz;
-    lr.name = "45Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr.desiredRefreshRate = 30_Hz;
-    lr.name = "30Hz Heuristic";
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr.desiredRefreshRate = 24_Hz;
-    lr.name = "24Hz Heuristic";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr.desiredRefreshRate = 24_Hz;
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr.name = "24Hz ExplicitExactOrMultiple";
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::Min;
-    lr2.vote = LayerVoteType::Max;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Min;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Min;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 24_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Max;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Max;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 15_Hz;
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 30_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 45_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers);
-        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
-    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60,
-                                       {.frameRateMultipleThreshold = 120});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers);
-        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, twoModes_getBestRefreshRate_Explicit) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 90_Hz;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
-        lr.desiredRefreshRate = Fps::fromValue(fps);
-        const auto mode = configs.getBestRefreshRate(layers, {});
-        EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
-                                 << to_string(mode->getFps());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::ExplicitDefault;
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.name = "90Hz ExplicitDefault";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Max;
-    lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 30_Hz;
-    lr1.name = "30Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 30_Hz;
-    lr1.name = "30Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Max;
-    lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::NoVote;
-    lr2.name = "NoVote";
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::NoVote;
-    lr2.name = "NoVote";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Max;
-    lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Max;
-    lr2.name = "Max";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    // The other layer starts to provide buffers
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 90_Hz;
-    lr2.name = "90Hz Heuristic";
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, touchConsidered) {
-    RefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    auto [_, signals] = configs.getBestRefreshRate({}, {});
-    EXPECT_FALSE(signals.touch);
-
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate({}, {.touch = true});
-    EXPECT_TRUE(signals.touch);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
-    EXPECT_TRUE(signals.touch);
-
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
-    EXPECT_FALSE(signals.touch);
-
-    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
-    EXPECT_TRUE(signals.touch);
-
-    lr1.vote = LayerVoteType::ExplicitDefault;
-    lr1.desiredRefreshRate = 60_Hz;
-    lr1.name = "60Hz ExplicitExactOrMultiple";
-    lr2.vote = LayerVoteType::Heuristic;
-    lr2.desiredRefreshRate = 60_Hz;
-    lr2.name = "60Hz Heuristic";
-    std::tie(std::ignore, signals) = configs.getBestRefreshRate(layers, {.touch = true});
-    EXPECT_FALSE(signals.touch);
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
-    TestableRefreshRateConfigs configs(kModes_60_90_72_120, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    // Prepare a table with the vote and the expected refresh rate
-    const std::initializer_list<std::pair<Fps, Fps>> testCases = {
-            {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
-
-            {100_Hz, 90_Hz},  {90_Hz, 90_Hz},   {89_Hz, 90_Hz},
-
-            {80_Hz, 72_Hz},   {73_Hz, 72_Hz},   {72_Hz, 72_Hz},   {71_Hz, 72_Hz},   {70_Hz, 72_Hz},
-
-            {65_Hz, 60_Hz},   {60_Hz, 60_Hz},   {59_Hz, 60_Hz},   {58_Hz, 60_Hz},
-
-            {55_Hz, 90_Hz},   {50_Hz, 90_Hz},   {45_Hz, 90_Hz},
-
-            {42_Hz, 120_Hz},  {40_Hz, 120_Hz},  {39_Hz, 120_Hz},
-
-            {37_Hz, 72_Hz},   {36_Hz, 72_Hz},   {35_Hz, 72_Hz},
-
-            {30_Hz, 60_Hz},
-    };
-
-    for (auto [desired, expected] : testCases) {
-        lr.vote = LayerVoteType::ExplicitDefault;
-        lr.desiredRefreshRate = desired;
-
-        std::stringstream ss;
-        ss << "ExplicitDefault " << desired;
-        lr.name = ss.str();
-
-        EXPECT_EQ(expected, configs.getBestRefreshRate(layers)->getFps());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest,
-       getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    // Test that 23.976 will choose 24 if 23.976 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-        lr.desiredRefreshRate = 23.976_Hz;
-        lr.name = "ExplicitExactOrMultiple 23.976 Hz";
-        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
-    }
-
-    // Test that 24 will choose 23.976 if 24 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.desiredRefreshRate = 24_Hz;
-        lr.name = "ExplicitExactOrMultiple 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
-    }
-
-    // Test that 29.97 will prefer 59.94 over 60 and 30
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode24Frac, kMode25, kMode30,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.desiredRefreshRate = 29.97_Hz;
-        lr.name = "ExplicitExactOrMultiple 29.97 Hz";
-        EXPECT_EQ(kModeId60Frac, configs.getBestRefreshRate(layers)->getId());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    // Test that voting for supported refresh rate will select this refresh rate
-    {
-        TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60);
-
-        for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
-            lr.vote = LayerVoteType::ExplicitExact;
-            lr.desiredRefreshRate = desired;
-            std::stringstream ss;
-            ss << "ExplicitExact " << desired;
-            lr.name = ss.str();
-
-            EXPECT_EQ(lr.desiredRefreshRate, configs.getBestRefreshRate(layers)->getFps());
-        }
-    }
-
-    // Test that 23.976 will choose 24 if 23.976 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.vote = LayerVoteType::ExplicitExact;
-        lr.desiredRefreshRate = 23.976_Hz;
-        lr.name = "ExplicitExact 23.976 Hz";
-        EXPECT_EQ(kModeId24, configs.getBestRefreshRate(layers)->getId());
-    }
-
-    // Test that 24 will choose 23.976 if 24 is not supported
-    {
-        TestableRefreshRateConfigs configs(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
-                                                     kMode60, kMode60Frac),
-                                           kModeId60);
-
-        lr.desiredRefreshRate = 24_Hz;
-        lr.name = "ExplicitExact 24 Hz";
-        EXPECT_EQ(kModeId24Frac, configs.getBestRefreshRate(layers)->getId());
-    }
-}
-
-TEST_F(RefreshRateConfigsTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz ExplicitDefault";
-    lr.focused = true;
-
-    const auto [mode, signals] = configs.getBestRefreshRate(layers, {.touch = true, .idle = true});
-
-    EXPECT_EQ(mode, kMode60);
-    EXPECT_FALSE(signals.touch);
-}
-
-TEST_F(RefreshRateConfigsTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}), 0);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = 90_Hz;
-    lr.name = "90Hz ExplicitDefault";
-    lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers, {.idle = true}));
-}
-
-TEST_F(RefreshRateConfigsTest,
-       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId90);
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
-    const auto [mode, signals] = configs.getBestRefreshRateAndSignals({}, {});
-    EXPECT_EQ(mode, kMode90);
-    EXPECT_FALSE(signals.touch);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& lr = layers[0];
-
-    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz ExplicitExactOrMultiple";
-    lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::ExplicitDefault;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz ExplicitDefault";
-    lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.focused = true;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Heuristic;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Heuristic";
-    lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Max;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Max";
-    lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.vote = LayerVoteType::Min;
-    lr.desiredRefreshRate = 60_Hz;
-    lr.name = "60Hz Min";
-    lr.focused = false;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    lr.focused = true;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    // The default policy doesn't allow group switching. Verify that no
-    // group switches are performed.
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = 90_Hz;
-    layer.seamlessness = Seamlessness::SeamedAndSeamless;
-    layer.name = "90Hz ExplicitDefault";
-    layer.focused = true;
-
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = 90_Hz;
-    layer.seamlessness = Seamlessness::SeamedAndSeamless;
-    layer.name = "90Hz ExplicitDefault";
-    layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    // Verify that we won't change the group if seamless switch is required.
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = 90_Hz;
-    layer.seamlessness = Seamlessness::OnlySeamless;
-    layer.name = "90Hz ExplicitDefault";
-    layer.focused = true;
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    configs.setActiveModeId(kModeId90);
-
-    // Verify that we won't do a seamless switch if we request the same mode as the default
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = 60_Hz;
-    layer.seamlessness = Seamlessness::OnlySeamless;
-    layer.name = "60Hz ExplicitDefault";
-    layer.focused = true;
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    configs.setActiveModeId(kModeId90);
-
-    // Verify that if the current config is in another group and there are no layers with
-    // seamlessness=SeamedAndSeamless we'll go back to the default group.
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitDefault;
-    layer.desiredRefreshRate = 60_Hz;
-    layer.seamlessness = Seamlessness::Default;
-    layer.name = "60Hz ExplicitDefault";
-    layer.focused = true;
-
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    configs.setActiveModeId(kModeId90);
-
-    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=OnlySeamless can't change the mode group.
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].seamlessness = Seamlessness::OnlySeamless;
-    layers[0].name = "60Hz ExplicitDefault";
-    layers[0].focused = true;
-
-    layers.push_back(LayerRequirement{.weight = 0.5f});
-    layers[1].vote = LayerVoteType::ExplicitDefault;
-    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = 90_Hz;
-    layers[1].name = "90Hz ExplicitDefault";
-    layers[1].focused = false;
-
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    configs.setActiveModeId(kModeId90);
-
-    // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
-    // seamlessness=Default can't change the mode group back to the group of the default
-    // mode.
-    // For example, this may happen when a video playback requests and gets a seamed switch,
-    // but another layer (with default seamlessness) starts animating. The animating layer
-    // should not cause a seamed switch.
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].seamlessness = Seamlessness::Default;
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].focused = true;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-    layers[0].name = "60Hz ExplicitDefault";
-
-    layers.push_back(LayerRequirement{.weight = 0.1f});
-    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = 90_Hz;
-    layers[1].focused = true;
-    layers[1].vote = LayerVoteType::ExplicitDefault;
-    layers[1].name = "90Hz ExplicitDefault";
-
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId60);
-
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    configs.setActiveModeId(kModeId90);
-
-    // Layer with seamlessness=Default can change the mode group if there's a not
-    // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
-    // when in split screen mode the user switches between the two visible applications.
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].seamlessness = Seamlessness::Default;
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].focused = true;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-    layers[0].name = "60Hz ExplicitDefault";
-
-    layers.push_back(LayerRequirement{.weight = 0.7f});
-    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
-    layers[1].desiredRefreshRate = 90_Hz;
-    layers[1].focused = false;
-    layers[1].vote = LayerVoteType::ExplicitDefault;
-    layers[1].name = "90Hz ExplicitDefault";
-
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
-    TestableRefreshRateConfigs configs(kModes_30_60, kModeId60);
-
-    // Allow group switching.
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    layer.desiredRefreshRate = 60_Hz;
-    layer.seamlessness = Seamlessness::SeamedAndSeamless;
-    layer.name = "60Hz ExplicitExactOrMultiple";
-    layer.focused = true;
-
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate(layers)->getId());
-
-    configs.setActiveModeId(kModeId120);
-    EXPECT_EQ(kModeId120, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
-    TestableRefreshRateConfigs configs(kModes_25_30_50_60, kModeId60);
-
-    // Allow group switching.
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
-                                             .vote = LayerVoteType::ExplicitDefault,
-                                             .desiredRefreshRate = 60_Hz,
-                                             .seamlessness = Seamlessness::SeamedAndSeamless,
-                                             .weight = 0.5f,
-                                             .focused = false},
-                                            {.name = "25Hz ExplicitExactOrMultiple",
-                                             .vote = LayerVoteType::ExplicitExactOrMultiple,
-                                             .desiredRefreshRate = 25_Hz,
-                                             .seamlessness = Seamlessness::OnlySeamless,
-                                             .weight = 1.f,
-                                             .focused = true}};
-
-    EXPECT_EQ(kModeId50, configs.getBestRefreshRate(layers)->getId());
-
-    auto& seamedLayer = layers[0];
-    seamedLayer.desiredRefreshRate = 30_Hz;
-    seamedLayer.name = "30Hz ExplicitDefault";
-    configs.setActiveModeId(kModeId30);
-
-    EXPECT_EQ(kModeId25, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
-    TestableRefreshRateConfigs configs(kModes_60_90_G1, kModeId90);
-
-    // Allow group switching.
-    RefreshRateConfigs::Policy policy;
-    policy.defaultMode = configs.getCurrentPolicy().defaultMode;
-    policy.allowGroupSwitching = true;
-    EXPECT_GE(configs.setDisplayManagerPolicy(policy), 0);
-
-    std::vector<LayerRequirement> layers = {
-            {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
-
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate(layers)->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
-    TestableRefreshRateConfigs configs(kModes_30_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].name = "Test layer";
-
-    struct Args {
-        bool touch = false;
-        bool focused = true;
-    };
-
-    // Return the config ID from calling getBestRefreshRate() for a single layer with the
-    // given voteType and fps.
-    auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId {
-        layers[0].vote = voteType;
-        layers[0].desiredRefreshRate = fps;
-        layers[0].focused = args.focused;
-        return configs.getBestRefreshRate(layers, {.touch = args.touch})->getId();
-    };
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}), 0);
-
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate()->getId());
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
-    EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
-    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
-
-    // Unfocused layers are not allowed to override primary config.
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
-    EXPECT_EQ(kModeId60,
-              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
-
-    // Touch boost should be restricted to the primary range.
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
-
-    // When we're higher than the primary range max due to a layer frame rate setting, touch boost
-    // shouldn't drag us back down to the primary range max.
-    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
-    EXPECT_EQ(kModeId60,
-              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}), 0);
-
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
-    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, idle) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].name = "Test layer";
-
-    const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
-        layers[0].vote = voteType;
-        layers[0].desiredRefreshRate = 90_Hz;
-
-        const auto [refreshRate, signals] =
-                configs.getBestRefreshRateAndSignals(layers, {.touch = touchActive, .idle = true});
-
-        // Refresh rate will be chosen by either touch state or idle state.
-        EXPECT_EQ(!touchActive, signals.idle);
-        return refreshRate->getId();
-    };
-
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}), 0);
-
-    // Idle should be lower priority than touch boost.
-    {
-        constexpr bool kTouchActive = true;
-        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
-        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
-        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
-        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
-        EXPECT_EQ(kModeId90, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
-        EXPECT_EQ(kModeId90,
-                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
-    }
-
-    // With no layers, idle should still be lower priority than touch boost.
-    EXPECT_EQ(kModeId90, configs.getBestRefreshRate({}, {.touch = true, .idle = true})->getId());
-
-    // Idle should be higher precedence than other layer frame rate considerations.
-    configs.setActiveModeId(kModeId90);
-
-    {
-        constexpr bool kTouchActive = false;
-        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::NoVote, kTouchActive));
-        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Min, kTouchActive));
-        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Max, kTouchActive));
-        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::Heuristic, kTouchActive));
-        EXPECT_EQ(kModeId60, getIdleFrameRate(LayerVoteType::ExplicitDefault, kTouchActive));
-        EXPECT_EQ(kModeId60,
-                  getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
-    }
-
-    // Idle should be applied rather than the current config when there are no layers.
-    EXPECT_EQ(kModeId60, configs.getBestRefreshRate({}, {.idle = true})->getId());
-}
-
-TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
-        const auto knownFrameRate = configs.findClosestKnownFrameRate(Fps::fromValue(fps));
-        const Fps expectedFrameRate = [fps] {
-            if (fps < 26.91f) return 24_Hz;
-            if (fps < 37.51f) return 30_Hz;
-            if (fps < 52.51f) return 45_Hz;
-            if (fps < 66.01f) return 60_Hz;
-            if (fps < 81.01f) return 72_Hz;
-            return 90_Hz;
-        }();
-
-        EXPECT_EQ(expectedFrameRate, knownFrameRate);
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
-    TestableRefreshRateConfigs configs(kModes_60_90, kModeId60);
-
-    struct Expectation {
-        Fps fps;
-        DisplayModePtr mode;
-    };
-
-    const std::initializer_list<Expectation> knownFrameRatesExpectations = {
-            {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90},
-            {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90},
-    };
-
-    // Make sure the test tests all the known frame rate
-    const auto& knownFrameRates = configs.knownFrameRates();
-    const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
-                                  knownFrameRatesExpectations.begin(),
-                                  [](Fps fps, const Expectation& expected) {
-                                      return isApproxEqual(fps, expected.fps);
-                                  });
-    EXPECT_TRUE(equal);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    auto& layer = layers[0];
-    layer.vote = LayerVoteType::Heuristic;
-
-    for (const auto& [fps, mode] : knownFrameRatesExpectations) {
-        layer.desiredRefreshRate = fps;
-        EXPECT_EQ(mode, configs.getBestRefreshRate(layers));
-    }
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    auto& explicitExactLayer = layers[0];
-    auto& explicitExactOrMultipleLayer = layers[1];
-
-    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
-    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
-    explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = 30_Hz;
-
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode30, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
-    explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60,
-                                       {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    auto& explicitExactLayer = layers[0];
-    auto& explicitExactOrMultipleLayer = layers[1];
-
-    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
-    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
-    explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = 30_Hz;
-
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
-    explicitExactLayer.desiredRefreshRate = 60_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_EQ(kMode72, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers));
-
-    explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCache) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
-    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
-    const auto args = std::make_pair(std::vector<LayerRequirement>{},
-                                     GlobalSignals{.touch = true, .idle = true});
-    const auto result = std::make_pair(kMode90, GlobalSignals{.touch = true});
-
-    configs.mutableGetBestRefreshRateCache() = {args, result};
-
-    EXPECT_EQ(result, configs.getBestRefreshRateAndSignals(args.first, args.second));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
-    TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60);
-
-    EXPECT_FALSE(configs.mutableGetBestRefreshRateCache());
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    RefreshRateConfigs::GlobalSignals globalSignals{.touch = true, .idle = true};
-
-    const auto result = configs.getBestRefreshRateAndSignals(layers, globalSignals);
-
-    const auto& cache = configs.mutableGetBestRefreshRateCache();
-    ASSERT_TRUE(cache);
-
-    EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
-    EXPECT_EQ(cache->result, result);
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
-    TestableRefreshRateConfigs configs(kModes_60_120, kModeId60, {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
-    auto& explicitExactLayer = layers[0];
-    auto& explicitExactOrMultipleLayer = layers[1];
-
-    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
-    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
-    explicitExactLayer.name = "ExplicitExact";
-    explicitExactLayer.desiredRefreshRate = 30_Hz;
-
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers, {.touch = true}));
-
-    explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
-
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers, {.touch = true}));
-}
-
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
-    TestableRefreshRateConfigs configs(kModes_24_25_30_50_60_Frac, kModeId60,
-                                       {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
-    auto& explicitDefaultLayer = layers[0];
-    auto& explicitExactOrMultipleLayer = layers[1];
-
-    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
-    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
-
-    explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
-    explicitDefaultLayer.name = "ExplicitDefault";
-    explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
-
-    EXPECT_EQ(kMode60, configs.getBestRefreshRate(layers));
-}
-
-// b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_withCloseRefreshRates) {
-    constexpr int kMinRefreshRate = 10;
-    constexpr int kMaxRefreshRate = 240;
-
-    DisplayModes displayModes;
-    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
-        const DisplayModeId modeId(fps);
-        displayModes.try_emplace(modeId,
-                                 createDisplayMode(modeId,
-                                                   Fps::fromValue(static_cast<float>(fps))));
-    }
-
-    const TestableRefreshRateConfigs configs(std::move(displayModes),
-                                             DisplayModeId(kMinRefreshRate));
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
-        layers[0].desiredRefreshRate = fps;
-        layers[0].vote = vote;
-        EXPECT_EQ(fps.getIntValue(), configs.getBestRefreshRate(layers)->getFps().getIntValue())
-                << "Failed for " << ftl::enum_string(vote);
-    };
-
-    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
-        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
-        testRefreshRate(refreshRate, LayerVoteType::Heuristic);
-        testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
-        testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
-        testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
-    }
-}
-
-// b/190578904
-TEST_F(RefreshRateConfigsTest, getBestRefreshRate_conflictingVotes) {
-    constexpr DisplayModeId kActiveModeId{0};
-    DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
-                                          createDisplayMode(DisplayModeId(1), 53_Hz),
-                                          createDisplayMode(DisplayModeId(2), 55_Hz),
-                                          createDisplayMode(DisplayModeId(3), 60_Hz));
-
-    const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false};
-    const TestableRefreshRateConfigs configs(std::move(displayModes), kActiveModeId);
-
-    const std::vector<LayerRequirement> layers = {
-            {
-                    .vote = LayerVoteType::ExplicitDefault,
-                    .desiredRefreshRate = 43_Hz,
-                    .seamlessness = Seamlessness::SeamedAndSeamless,
-                    .weight = 0.41f,
-            },
-            {
-                    .vote = LayerVoteType::ExplicitExactOrMultiple,
-                    .desiredRefreshRate = 53_Hz,
-                    .seamlessness = Seamlessness::SeamedAndSeamless,
-                    .weight = 0.41f,
-            },
-    };
-
-    EXPECT_EQ(53_Hz, configs.getBestRefreshRate(layers, globalSignals)->getFps());
-}
-
-TEST_F(RefreshRateConfigsTest, modeComparison) {
-    EXPECT_LT(kMode60->getFps(), kMode90->getFps());
-    EXPECT_GE(kMode60->getFps(), kMode60->getFps());
-    EXPECT_GE(kMode90->getFps(), kMode90->getFps());
-}
-
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
-    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
-
-    RefreshRateConfigs configs(kModes_60_90, kModeId90);
-
-    // SetPolicy(60, 90), current 90Hz => TurnOn.
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
-    // SetPolicy(60, 90), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
-    // SetPolicy(60, 60), current 60Hz => TurnOff
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-
-    // SetPolicy(90, 90), current 90Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-}
-
-TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
-    using KernelIdleTimerAction = RefreshRateConfigs::KernelIdleTimerAction;
-
-    RefreshRateConfigs configs(kModes_60_120, kModeId120);
-
-    // SetPolicy(0, 60), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
-    // SetPolicy(60, 60), current 60Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-
-    // SetPolicy(60, 120), current 60Hz => TurnOn.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOn, configs.getIdleTimerAction());
-
-    // SetPolicy(120, 120), current 120Hz => TurnOff.
-    EXPECT_GE(configs.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}), 0);
-    EXPECT_EQ(KernelIdleTimerAction::TurnOff, configs.getIdleTimerAction());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateDivisor) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId30);
-
-    const auto frameRate = 30_Hz;
-    Fps displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
-    configs.setActiveModeId(kModeId60);
-    displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(2, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
-    configs.setActiveModeId(kModeId72);
-    displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
-    configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(3, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
-    configs.setActiveModeId(kModeId120);
-    displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, frameRate));
-
-    configs.setActiveModeId(kModeId90);
-    displayRefreshRate = configs.getActiveMode()->getFps();
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
-
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 25_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(24_Hz, 23.976_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(30_Hz, 29.97_Hz));
-    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivisor(60_Hz, 59.94_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
-
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
-
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
-
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
-
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
-    EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
-
-    const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
-    for (auto refreshRate : refreshRates) {
-        EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate));
-    }
-
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz));
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
-    EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120);
-
-    EXPECT_TRUE(configs.getFrameRateOverrides({}, 120_Hz, {}).empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
-    layers[0].name = "Test layer";
-    layers[0].ownerUid = 1234;
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    layers[0].vote = LayerVoteType::NoVote;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Min;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Max;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-
-    layers[0].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
-                                            {.ownerUid = 5678, .weight = 1.f}};
-
-    layers[0].name = "Test layer 1234";
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-
-    layers[1].name = "Test layer 5678";
-    layers[1].desiredRefreshRate = 30_Hz;
-    layers[1].vote = LayerVoteType::ExplicitDefault;
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_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));
-
-    layers[1].vote = LayerVoteType::Heuristic;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    layers[1].ownerUid = 1234;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
-    RefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId120,
-                               {.enableFrameRateOverride = true});
-
-    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
-    layers[0].name = "Test layer";
-    layers[0].desiredRefreshRate = 60_Hz;
-    layers[0].vote = LayerVoteType::ExplicitDefault;
-
-    auto frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    layers[0].vote = LayerVoteType::ExplicitExact;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {});
-    EXPECT_EQ(1u, frameRateOverrides.size());
-    ASSERT_EQ(1u, frameRateOverrides.count(1234));
-    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
-
-    frameRateOverrides = configs.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
-    EXPECT_TRUE(frameRateOverrides.empty());
-}
-
-} // namespace
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
new file mode 100644
index 0000000..d63e187
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -0,0 +1,3046 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <algorithm>
+#include <array>
+
+#include <ftl/enum.h>
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <ui/Size.h>
+
+#include <scheduler/FrameRateMode.h>
+#include "DisplayHardware/HWC2.h"
+#include "FpsOps.h"
+#include "Scheduler/RefreshRateSelector.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+using Config = RefreshRateSelector::Config;
+using LayerRequirement = RefreshRateSelector::LayerRequirement;
+using LayerVoteType = RefreshRateSelector::LayerVoteType;
+using SetPolicyResult = RefreshRateSelector::SetPolicyResult;
+
+using mock::createDisplayMode;
+
+struct TestableRefreshRateSelector : RefreshRateSelector {
+    using RefreshRateSelector::FrameRateRanking;
+    using RefreshRateSelector::RefreshRateOrder;
+
+    using RefreshRateSelector::RefreshRateSelector;
+
+    void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateSelector::setActiveMode(modeId, renderFrameRate);
+    }
+
+    const DisplayMode& getActiveMode() const {
+        std::lock_guard lock(mLock);
+        return *RefreshRateSelector::getActiveModeLocked().modePtr;
+    }
+
+    ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const {
+        std::lock_guard lock(mLock);
+        return ftl::as_non_null(mMinRefreshRateModeIt->second);
+    }
+
+    ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const {
+        std::lock_guard lock(mLock);
+        return ftl::as_non_null(mMaxRefreshRateModeIt->second);
+    }
+
+    ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const {
+        std::lock_guard lock(mLock);
+        return ftl::as_non_null(getMinRefreshRateByPolicyLocked());
+    }
+
+    ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const {
+        std::lock_guard lock(mLock);
+        return ftl::as_non_null(
+                getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()));
+    }
+
+    FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
+                                      RefreshRateOrder refreshRateOrder) const {
+        std::lock_guard lock(mLock);
+        return RefreshRateSelector::rankFrameRates(anchorGroupOpt, refreshRateOrder);
+    }
+
+    const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
+
+    using RefreshRateSelector::GetRankedFrameRatesCache;
+    auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+
+    auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
+                             GlobalSignals signals) const {
+        const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
+
+        EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
+                                   ScoredFrameRate::DescendingScore{}));
+
+        return result;
+    }
+
+    auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
+                                     GlobalSignals signals) const {
+        const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
+        return std::make_pair(ranking, consideredSignals);
+    }
+
+    ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
+            const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
+        return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
+    }
+
+    ScoredFrameRate getBestScoredFrameRate(const std::vector<LayerRequirement>& layers = {},
+                                           GlobalSignals signals = {}) const {
+        return getRankedFrameRates(layers, signals).ranking.front();
+    }
+
+    SetPolicyResult setPolicy(const PolicyVariant& policy) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        return RefreshRateSelector::setPolicy(policy);
+    }
+
+    SetPolicyResult setDisplayManagerPolicy(const DisplayManagerPolicy& policy) {
+        return setPolicy(policy);
+    }
+
+    const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+};
+
+class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
+protected:
+    using RefreshRateOrder = TestableRefreshRateSelector::RefreshRateOrder;
+
+    RefreshRateSelectorTest();
+    ~RefreshRateSelectorTest();
+
+    static constexpr DisplayModeId kModeId60{0};
+    static constexpr DisplayModeId kModeId90{1};
+    static constexpr DisplayModeId kModeId72{2};
+    static constexpr DisplayModeId kModeId120{3};
+    static constexpr DisplayModeId kModeId30{4};
+    static constexpr DisplayModeId kModeId25{5};
+    static constexpr DisplayModeId kModeId50{6};
+    static constexpr DisplayModeId kModeId24{7};
+    static constexpr DisplayModeId kModeId24Frac{8};
+    static constexpr DisplayModeId kModeId30Frac{9};
+    static constexpr DisplayModeId kModeId60Frac{10};
+    static constexpr DisplayModeId kModeId35{11};
+    static constexpr DisplayModeId kModeId1{12};
+    static constexpr DisplayModeId kModeId5{13};
+    static constexpr DisplayModeId kModeId10{14};
+
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode60Frac =
+            ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_4K =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30Frac =
+            ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode35 =
+            ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode50 =
+            ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24 =
+            ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
+            ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode1 =
+            ftl::as_non_null(createDisplayMode(kModeId1, 1_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode5 =
+            ftl::as_non_null(createDisplayMode(kModeId5, 5_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode10 =
+            ftl::as_non_null(createDisplayMode(kModeId10, 10_Hz));
+
+    // Test configurations.
+    static inline const DisplayModes kModes_60 = makeModes(kMode60);
+    static inline const DisplayModes kModes_35_60_90 = makeModes(kMode35, kMode60, kMode90);
+    static inline const DisplayModes kModes_60_90 = makeModes(kMode60, kMode90);
+    static inline const DisplayModes kModes_60_90_G1 = makeModes(kMode60, kMode90_G1);
+    static inline const DisplayModes kModes_60_90_4K = makeModes(kMode60, kMode90_4K);
+    static inline const DisplayModes kModes_60_72_90 = makeModes(kMode60, kMode90, kMode72);
+    static inline const DisplayModes kModes_60_90_72_120 =
+            makeModes(kMode60, kMode90, kMode72, kMode120);
+    static inline const DisplayModes kModes_30_60_72_90_120 =
+            makeModes(kMode60, kMode90, kMode72, kMode120, kMode30);
+
+    static inline const DisplayModes kModes_30_60 =
+            makeModes(kMode60, kMode90_G1, kMode72_G1, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_30_60_72_90 =
+            makeModes(kMode60, kMode90, kMode72, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_30_60_90 =
+            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30);
+    static inline const DisplayModes kModes_25_30_50_60 =
+            makeModes(kMode60, kMode90, kMode72_G1, kMode120_G1, kMode30_G1, kMode25_G1, kMode50);
+    static inline const DisplayModes kModes_60_120 = makeModes(kMode60, kMode120);
+    static inline const DisplayModes kModes_1_5_10 = makeModes(kMode1, kMode5, kMode10);
+
+    // This is a typical TV configuration.
+    static inline const DisplayModes kModes_24_25_30_50_60_Frac =
+            makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode30Frac, kMode50, kMode60,
+                      kMode60Frac);
+
+    static TestableRefreshRateSelector createSelector(DisplayModes modes,
+                                                      DisplayModeId activeModeId,
+                                                      Config config = {}) {
+        config.enableFrameRateOverride = GetParam();
+        return TestableRefreshRateSelector(modes, activeModeId, config);
+    }
+};
+
+RefreshRateSelectorTest::RefreshRateSelectorTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+RefreshRateSelectorTest::~RefreshRateSelectorTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+
+INSTANTIATE_TEST_SUITE_P(PerOverrideConfig, RefreshRateSelectorTest,
+                         testing::Values(Config::FrameRateOverride::Disabled,
+                                         Config::FrameRateOverride::AppOverrideNativeRefreshRates,
+                                         Config::FrameRateOverride::AppOverride,
+                                         Config::FrameRateOverride::Enabled));
+
+TEST_P(RefreshRateSelectorTest, oneMode_canSwitch) {
+    auto selector = createSelector(kModes_60, kModeId60);
+    if (GetParam() == Config::FrameRateOverride::Enabled) {
+        EXPECT_TRUE(selector.canSwitch());
+    } else {
+        EXPECT_FALSE(selector.canSwitch());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, invalidPolicy) {
+    auto selector = createSelector(kModes_60, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              selector.setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}));
+    EXPECT_EQ(SetPolicyResult::Invalid,
+              selector.setDisplayManagerPolicy({kModeId60, {20_Hz, 40_Hz}}));
+}
+
+TEST_P(RefreshRateSelectorTest, unchangedPolicy) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+
+    // Override to the same policy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              selector.setPolicy(RefreshRateSelector::OverridePolicy{kModeId90, {60_Hz, 90_Hz}}));
+
+    // Clear override to restore DisplayManagerPolicy.
+    EXPECT_EQ(SetPolicyResult::Unchanged,
+              selector.setPolicy(RefreshRateSelector::NoOverridePolicy{}));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {30_Hz, 90_Hz}}));
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    const auto minRate = selector.getMinSupportedRefreshRate();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode90, performanceRate);
+
+    const auto minRateByPolicy = selector.getMinRefreshRateByPolicy();
+    const auto performanceRateByPolicy = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(minRateByPolicy, minRate);
+    EXPECT_EQ(performanceRateByPolicy, performanceRate);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentGroups) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    const auto minRate90 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode90_G1, performanceRate);
+    EXPECT_EQ(kMode90_G1, minRate90);
+    EXPECT_EQ(kMode90_G1, performanceRate90);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_storesFullRefreshRateMap_differentResolutions) {
+    auto selector = createSelector(kModes_60_90_4K, kModeId60);
+
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxSupportedRefreshRate();
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    const auto minRate90 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode90_4K, performanceRate);
+    EXPECT_EQ(kMode90_4K, minRate90);
+    EXPECT_EQ(kMode90_4K, performanceRate90);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_policyChange) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    const auto minRate = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode60, minRate);
+    EXPECT_EQ(kMode90, performanceRate);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+    const auto minRate60 = selector.getMinRefreshRateByPolicy();
+    const auto performanceRate60 = selector.getMaxRefreshRateByPolicy();
+
+    EXPECT_EQ(kMode60, minRate60);
+    EXPECT_EQ(kMode60, performanceRate60);
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_getActiveMode) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+    {
+        const auto& mode = selector.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId60);
+    }
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+    {
+        const auto& mode = selector.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
+    }
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+    {
+        const auto& mode = selector.getActiveMode();
+        EXPECT_EQ(mode.getId(), kModeId90);
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_noLayers) {
+    {
+        auto selector = createSelector(kModes_60_72_90, kModeId72);
+
+        // If there are no layers we select the default frame rate, which is the max of the primary
+        // range.
+        EXPECT_EQ(kMode90, selector.getBestFrameRateMode());
+
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+        EXPECT_EQ(kMode60, selector.getBestFrameRateMode());
+    }
+    {
+        // We select max even when this will cause a non-seamless switch.
+        auto selector = createSelector(kModes_60_90_G1, kModeId60);
+        constexpr bool kAllowGroupSwitching = true;
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
+        EXPECT_EQ(kMode90_G1, selector.getBestFrameRateMode());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_exactDontChangeRefreshRateWhenNotInPolicy) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId72);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    layers[0].desiredRefreshRate = 120_Hz;
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId72, {0_Hz, 90_Hz}}));
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_90) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.name = "";
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 120_Hz}}));
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multipleThreshold_60_90) {
+    auto selector = createSelector(kModes_60_90, kModeId60, {.frameRateMultipleThreshold = 90});
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_60_72_90) {
+    auto selector = createSelector(kModes_60_72_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90_120) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_90_120_DifferentTypes) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "60Hz ExplicitDefault";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.name = "24Hz Heuristic";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "90Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_30_60_90_120_DifferentTypes_multipleThreshold) {
+    auto selector =
+            createSelector(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120});
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "60Hz ExplicitDefault";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.name = "24Hz Heuristic";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "90Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "120Hz ExplicitDefault";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 24_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::ExplicitExact;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 10_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "120Hz ExplicitExact";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 30_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "30Hz ExplicitExactOrMultiple";
+    lr3.vote = LayerVoteType::Heuristic;
+    lr3.desiredRefreshRate = 120_Hz;
+    lr3.name = "120Hz Heuristic";
+    EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60) {
+    auto selector = createSelector(kModes_30_60, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 30_Hz;
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_30_60_72_90) {
+    auto selector = createSelector(kModes_30_60_72_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 90_Hz;
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr.desiredRefreshRate = 45_Hz;
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr.desiredRefreshRate = 30_Hz;
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr.desiredRefreshRate = 24_Hz;
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr.desiredRefreshRate = 24_Hz;
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr.name = "24Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(kMode72, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_PriorityTest) {
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 24_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 15_Hz;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 45_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto mode = selector.getBestFrameRateMode(layers);
+        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_24FpsVideo_multipleThreshold_60_120) {
+    auto selector = createSelector(kModes_60_120, kModeId60, {.frameRateMultipleThreshold = 120});
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto mode = selector.getBestFrameRateMode(layers);
+        EXPECT_EQ(kMode60, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, twoModes_getBestFrameRateMode_Explicit) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 90_Hz;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60_Hz;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_75HzContent) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = Fps::fromValue(fps);
+        const auto mode = selector.getBestFrameRateMode(layers, {});
+        EXPECT_EQ(kMode90, mode) << lr.desiredRefreshRate << " chooses "
+                                 << to_string(mode->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_Multiples) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::NoVote;
+    lr2.name = "NoVote";
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::NoVote;
+    lr2.name = "NoVote";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.touch = true}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    // The other layer starts to provide buffers
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90_Hz;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
+    const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+                                                        RefreshRateOrder::Descending);
+
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicy) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+    const auto refreshRates = selector.rankRefreshRates(selector.getActiveMode().getGroup(),
+                                                        RefreshRateOrder::Ascending);
+
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getMinRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    auto selector = createSelector(kModes_30_60_90, kModeId72);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
+
+    const auto refreshRates =
+            selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
+
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60}, {90_Hz, kMode90}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicyOutsideTheGroup) {
+    // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the
+    // different group.
+    auto selector = createSelector(kModes_30_60_90, kModeId72);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
+
+    const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
+                                                        RefreshRateOrder::Descending);
+
+    const auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}, {45_Hz, kMode90}, {30_Hz, kMode30}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
+    EXPECT_FALSE(signals.powerOnImminent);
+
+    auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60},   {45_Hz, kMode90},
+                        {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    std::tie(refreshRates, signals) =
+            selector.getRankedRefreshRatesAsPair({}, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr1 = layers[0];
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+
+    std::tie(refreshRates, signals) =
+            selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = true});
+    EXPECT_TRUE(signals.powerOnImminent);
+
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    std::tie(refreshRates, signals) =
+            selector.getRankedRefreshRatesAsPair(layers, {.powerOnImminent = false});
+    EXPECT_FALSE(signals.powerOnImminent);
+
+    expectedRefreshRates = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{60_Hz, kMode60}, {90_Hz, kMode90}};
+            case Config::FrameRateOverride::Enabled:
+                return {{60_Hz, kMode60}, {90_Hz, kMode90},   {45_Hz, kMode90},
+                        {30_Hz, kMode60}, {22.5_Hz, kMode90}, {20_Hz, kMode60}};
+        }
+    }();
+    ASSERT_EQ(expectedRefreshRates.size(), refreshRates.size());
+
+    for (size_t i = 0; i < expectedRefreshRates.size(); ++i) {
+        EXPECT_EQ(expectedRefreshRates[i], refreshRates[i].frameRateMode)
+                << "Expected " << expectedRefreshRates[i].fps.getIntValue() << " ("
+                << expectedRefreshRates[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << refreshRates[i].frameRateMode.fps.getIntValue() << " ("
+                << refreshRates[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, touchConsidered) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    auto [_, signals] = selector.getRankedFrameRates({}, {});
+    EXPECT_FALSE(signals.touch);
+
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
+    EXPECT_TRUE(signals.touch);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz Heuristic";
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    EXPECT_TRUE(signals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitDefault";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz Heuristic";
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    EXPECT_FALSE(signals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz Heuristic";
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    EXPECT_TRUE(signals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60_Hz;
+    lr1.name = "60Hz ExplicitDefault";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz Heuristic";
+    std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair(layers, {.touch = true});
+    EXPECT_FALSE(signals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
+    auto selector = createSelector(kModes_60_90_72_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    // Prepare a table with the vote and the expected refresh rate
+    const std::initializer_list<std::pair<Fps, Fps>> testCases = {
+            {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
+
+            {100_Hz, 90_Hz},  {90_Hz, 90_Hz},   {89_Hz, 90_Hz},
+
+            {80_Hz, 72_Hz},   {73_Hz, 72_Hz},   {72_Hz, 72_Hz},   {71_Hz, 72_Hz},   {70_Hz, 72_Hz},
+
+            {65_Hz, 60_Hz},   {60_Hz, 60_Hz},   {59_Hz, 60_Hz},   {58_Hz, 60_Hz},
+
+            {55_Hz, 90_Hz},   {50_Hz, 90_Hz},   {45_Hz, 90_Hz},
+
+            {42_Hz, 120_Hz},  {40_Hz, 120_Hz},  {39_Hz, 120_Hz},
+
+            {37_Hz, 72_Hz},   {36_Hz, 72_Hz},   {35_Hz, 72_Hz},
+
+            {30_Hz, 60_Hz},
+    };
+
+    for (auto [desired, expected] : testCases) {
+        lr.vote = LayerVoteType::ExplicitDefault;
+        lr.desiredRefreshRate = desired;
+
+        std::stringstream ss;
+        ss << "ExplicitDefault " << desired;
+        lr.name = ss.str();
+
+        EXPECT_EQ(expected, selector.getBestFrameRateMode(layers)->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    // Test that 23.976 will choose 24 if 23.976 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode24, kMode25, kMode30, kMode30Frac, kMode60,
+                                                 kMode60Frac),
+                                       kModeId60);
+
+        lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+        lr.desiredRefreshRate = 23.976_Hz;
+        lr.name = "ExplicitExactOrMultiple 23.976 Hz";
+        EXPECT_EQ(kModeId24, selector.getBestFrameRateMode(layers)->getId());
+    }
+
+    // Test that 24 will choose 23.976 if 24 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode24Frac, kMode25, kMode30, kMode30Frac,
+                                                 kMode60, kMode60Frac),
+                                       kModeId60);
+
+        lr.desiredRefreshRate = 24_Hz;
+        lr.name = "ExplicitExactOrMultiple 24 Hz";
+        EXPECT_EQ(kModeId24Frac, selector.getBestFrameRateMode(layers)->getId());
+    }
+
+    // Test that 29.97 will prefer 59.94 over 60 and 30
+    {
+        auto selector = createSelector(makeModes(kMode24, kMode24Frac, kMode25, kMode30, kMode60,
+                                                 kMode60Frac),
+                                       kModeId60);
+
+        lr.desiredRefreshRate = 29.97_Hz;
+        lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+        EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId());
+    }
+
+    // Test that 29.97 will choose 30 if 59.94 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60);
+
+        lr.desiredRefreshRate = 29.97_Hz;
+        lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+        EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId());
+    }
+
+    // Test that 59.94 will choose 60 if 59.94 is not supported
+    {
+        auto selector = createSelector(makeModes(kMode60, kMode30Frac, kMode30), kModeId60);
+
+        lr.desiredRefreshRate = 59.94_Hz;
+        lr.name = "ExplicitExactOrMultiple 59.94 Hz";
+        EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) {
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    // Test that voting for supported refresh rate will select this refresh rate
+    {
+        auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
+
+        for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
+            lr.vote = LayerVoteType::ExplicitExact;
+            lr.desiredRefreshRate = desired;
+            std::stringstream ss;
+            ss << "ExplicitExact " << desired;
+            lr.name = ss.str();
+
+            EXPECT_EQ(lr.desiredRefreshRate, selector.getBestFrameRateMode(layers)->getFps());
+        }
+    }
+}
+
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+    auto selector = createSelector(kModes_60_90, kModeId90);
+
+    constexpr FpsRange k90 = {90_Hz, 90_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz ExplicitDefault";
+    lr.focused = true;
+
+    const auto [rankedFrameRate, signals] =
+            selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
+
+    EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
+    EXPECT_FALSE(signals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    constexpr FpsRange k60 = {60_Hz, 60_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {k60, k60}, {k60_90, k60_90}}));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 90_Hz;
+    lr.name = "90Hz ExplicitDefault";
+    lr.focused = true;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers, {.idle = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, testDisplayModeOrdering) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f},
+                                            {.weight = 1.f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+    auto& lr3 = layers[2];
+    auto& lr4 = layers[3];
+    auto& lr5 = layers[4];
+
+    lr1.desiredRefreshRate = 90_Hz;
+    lr1.name = "90Hz";
+    lr1.focused = true;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.focused = true;
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+    lr3.focused = true;
+
+    lr4.desiredRefreshRate = 120_Hz;
+    lr4.name = "120Hz";
+    lr4.focused = true;
+
+    lr5.desiredRefreshRate = 30_Hz;
+    lr5.name = "30Hz";
+    lr5.focused = true;
+
+    auto expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{120_Hz, kMode120},
+                        {90_Hz, kMode90},
+                        {72_Hz, kMode72},
+                        {60_Hz, kMode60},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{120_Hz, kMode120}, {90_Hz, kMode90},  {72_Hz, kMode72}, {60_Hz, kMode60},
+                        {45_Hz, kMode90},   {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+        }
+    }();
+
+    auto actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+    ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+    for (size_t i = 0; i < expectedRanking.size(); ++i) {
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    lr1.vote = LayerVoteType::Max;
+    lr1.name = "Max";
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+
+    lr3.desiredRefreshRate = 72_Hz;
+    lr3.name = "72Hz";
+
+    lr4.desiredRefreshRate = 90_Hz;
+    lr4.name = "90Hz";
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz";
+
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{120_Hz, kMode120},
+                        {90_Hz, kMode90},
+                        {72_Hz, kMode72},
+                        {60_Hz, kMode60},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{120_Hz, kMode120}, {90_Hz, kMode90},  {72_Hz, kMode72}, {60_Hz, kMode60},
+                        {45_Hz, kMode90},   {40_Hz, kMode120}, {36_Hz, kMode72}, {30_Hz, kMode30}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+    ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+    for (size_t i = 0; i < expectedRanking.size(); ++i) {
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30_Hz;
+    lr1.name = "30Hz";
+
+    lr2.desiredRefreshRate = 120_Hz;
+    lr2.name = "120Hz";
+
+    lr3.desiredRefreshRate = 60_Hz;
+    lr3.name = "60Hz";
+
+    lr5.desiredRefreshRate = 72_Hz;
+    lr5.name = "72Hz";
+
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, kMode30},
+                        {60_Hz, kMode60},
+                        {90_Hz, kMode90},
+                        {120_Hz, kMode120},
+                        {72_Hz, kMode72}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, kMode30}, {60_Hz, kMode60},  {90_Hz, kMode90}, {120_Hz, kMode120},
+                        {45_Hz, kMode90}, {40_Hz, kMode120}, {72_Hz, kMode72}, {36_Hz, kMode72}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+    ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+    for (size_t i = 0; i < expectedRanking.size(); ++i) {
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+
+    lr1.desiredRefreshRate = 120_Hz;
+    lr1.name = "120Hz";
+    lr1.weight = 0.0f;
+
+    lr2.desiredRefreshRate = 60_Hz;
+    lr2.name = "60Hz";
+    lr2.vote = LayerVoteType::NoVote;
+
+    lr3.name = "60Hz-2";
+    lr3.vote = LayerVoteType::Heuristic;
+
+    lr4.vote = LayerVoteType::ExplicitExact;
+
+    lr5.desiredRefreshRate = 120_Hz;
+    lr5.name = "120Hz-2";
+
+    expectedRanking = []() -> std::vector<FrameRateMode> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{90_Hz, kMode90},
+                        {60_Hz, kMode60},
+                        {120_Hz, kMode120},
+                        {72_Hz, kMode72},
+                        {30_Hz, kMode30}};
+            case Config::FrameRateOverride::Enabled:
+                return {{90_Hz, kMode90}, {60_Hz, kMode60},  {120_Hz, kMode120}, {72_Hz, kMode72},
+                        {45_Hz, kMode90}, {40_Hz, kMode120}, {36_Hz, kMode72},   {30_Hz, kMode30}};
+        }
+    }();
+    actualRanking = selector.getRankedFrameRates(layers, {}).ranking;
+
+    ASSERT_EQ(expectedRanking.size(), actualRanking.size());
+
+    for (size_t i = 0; i < expectedRanking.size(); ++i) {
+        EXPECT_EQ(expectedRanking[i], actualRanking[i].frameRateMode)
+                << "Expected " << expectedRanking[i].fps.getIntValue() << " ("
+                << expectedRanking[i].modePtr->getFps().getIntValue() << ")"
+                << " Actual " << actualRanking[i].frameRateMode.fps.getIntValue() << " ("
+                << actualRanking[i].frameRateMode.modePtr->getFps().getIntValue() << ")";
+    }
+}
+
+TEST_P(RefreshRateSelectorTest,
+       getBestFrameRateMode_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+    auto selector = createSelector(kModes_60_90, kModeId90);
+
+    constexpr FpsRange k90 = {90_Hz, 90_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
+
+    const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+    EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
+    EXPECT_FALSE(signals.touch);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz ExplicitExactOrMultiple";
+    lr.focused = false;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.focused = true;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz ExplicitDefault";
+    lr.focused = false;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.focused = true;
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Heuristic;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Heuristic";
+    lr.focused = false;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.focused = true;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Max;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Max";
+    lr.focused = false;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.focused = true;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.vote = LayerVoteType::Min;
+    lr.desiredRefreshRate = 60_Hz;
+    lr.name = "60Hz Min";
+    lr.focused = false;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+
+    lr.focused = true;
+    EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers));
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingNotAllowed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    // The default policy doesn't allow group switching. Verify that no
+    // group switches are performed.
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 90_Hz;
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
+
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayer) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 90_Hz;
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamless) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    // Verify that we won't change the group if seamless switch is required.
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 90_Hz;
+    layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "90Hz ExplicitDefault";
+    layer.focused = true;
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    // Verify that we won't do a seamless switch if we request the same mode as the default
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 60_Hz;
+    layer.seamlessness = Seamlessness::OnlySeamless;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    // Verify that if the active mode is in another group and there are no layers with
+    // Seamlessness::SeamedAndSeamless, we should switch back to the default group.
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 60_Hz;
+    layer.seamlessness = Seamlessness::Default;
+    layer.name = "60Hz ExplicitDefault";
+    layer.focused = true;
+
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
+    // Seamlessness::OnlySeamless can't change the mode group.
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].seamlessness = Seamlessness::OnlySeamless;
+    layers[0].name = "60Hz ExplicitDefault";
+    layers[0].focused = true;
+
+    layers.push_back(LayerRequirement{.weight = 0.5f});
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = 90_Hz;
+    layers[1].name = "90Hz ExplicitDefault";
+    layers[1].focused = false;
+
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
+    // Seamlessness::Default can't change the mode group back to the group of the default
+    // mode.
+    // For example, this may happen when a video playback requests and gets a seamed switch,
+    // but another layer (with default seamlessness) starts animating. The animating layer
+    // should not cause a seamed switch.
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.1f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = 90_Hz;
+    layers[1].focused = true;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId60);
+
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    // Layer with Seamlessness::Default can change the mode group if there's an
+    // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
+    // when in split screen mode the user switches between the two visible applications.
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].seamlessness = Seamlessness::Default;
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].focused = true;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    layers[0].name = "60Hz ExplicitDefault";
+
+    layers.push_back(LayerRequirement{.weight = 0.7f});
+    layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+    layers[1].desiredRefreshRate = 90_Hz;
+    layers[1].focused = false;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    layers[1].name = "90Hz ExplicitDefault";
+
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, nonSeamlessVotePrefersSeamlessSwitches) {
+    auto selector = createSelector(kModes_30_60, kModeId60);
+
+    // Allow group switching.
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    layer.desiredRefreshRate = 60_Hz;
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
+    layer.name = "60Hz ExplicitExactOrMultiple";
+    layer.focused = true;
+
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
+
+    selector.setActiveMode(kModeId120, 120_Hz);
+    EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, nonSeamlessExactAndSeamlessMultipleLayers) {
+    auto selector = createSelector(kModes_25_30_50_60, kModeId60);
+
+    // Allow group switching.
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
+                                             .vote = LayerVoteType::ExplicitDefault,
+                                             .desiredRefreshRate = 60_Hz,
+                                             .seamlessness = Seamlessness::SeamedAndSeamless,
+                                             .weight = 0.5f,
+                                             .focused = false},
+                                            {.name = "25Hz ExplicitExactOrMultiple",
+                                             .vote = LayerVoteType::ExplicitExactOrMultiple,
+                                             .desiredRefreshRate = 25_Hz,
+                                             .seamlessness = Seamlessness::OnlySeamless,
+                                             .weight = 1.f,
+                                             .focused = true}};
+
+    EXPECT_EQ(kModeId50, selector.getBestFrameRateMode(layers)->getId());
+
+    auto& seamedLayer = layers[0];
+    seamedLayer.desiredRefreshRate = 30_Hz;
+    seamedLayer.name = "30Hz ExplicitDefault";
+    selector.setActiveMode(kModeId30, 30_Hz);
+
+    EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, minLayersDontTrigerSeamedSwitch) {
+    auto selector = createSelector(kModes_60_90_G1, kModeId90);
+
+    // Allow group switching.
+    RefreshRateSelector::DisplayManagerPolicy policy;
+    policy.defaultMode = selector.getCurrentPolicy().defaultMode;
+    policy.allowGroupSwitching = true;
+    EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
+
+    std::vector<LayerRequirement> layers = {
+            {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
+
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode(layers)->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, primaryVsAppRequestPolicy) {
+    auto selector = createSelector(kModes_30_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+
+    struct Args {
+        bool touch = false;
+        bool focused = true;
+    };
+
+    // Returns the mode selected by getBestFrameRateMode for a single layer with the given
+    // arguments.
+    const auto getFrameRate = [&](LayerVoteType voteType, Fps fps,
+                                  Args args = {}) -> DisplayModeId {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = fps;
+        layers[0].focused = args.focused;
+        return selector.getBestFrameRateMode(layers, {.touch = args.touch})->getId();
+    };
+
+    constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
+    constexpr FpsRange k30_90 = {30_Hz, 90_Hz};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
+
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode()->getId());
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(kModeId30, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+
+    // Unfocused layers are not allowed to override primary range.
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
+    EXPECT_EQ(kModeId60,
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
+
+    // Touch boost should be restricted to the primary range.
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
+
+    // When we're higher than the primary range max due to a layer frame rate setting, touch boost
+    // shouldn't drag us back down to the primary range max.
+    EXPECT_EQ(kModeId90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
+    EXPECT_EQ(kModeId60,
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Max, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+    EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, idle) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+
+    const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+                                          bool touchActive) -> DisplayModeId {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = 90_Hz;
+
+        const auto [ranking, signals] =
+                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+        // Refresh rate will be chosen by either touch state or idle state.
+        EXPECT_EQ(!touchActive, signals.idle);
+        return ranking.front().frameRateMode.modePtr->getId();
+    };
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
+
+    // Idle should be lower priority than touch boost.
+    {
+        constexpr bool kTouchActive = true;
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId90, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId90,
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+    // Idle should be higher precedence than other layer frame rate considerations.
+    selector.setActiveMode(kModeId90, 90_Hz);
+
+    {
+        constexpr bool kTouchActive = false;
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId60, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId60,
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
+
+    // Idle should be applied rather than the active mode when there are no layers.
+    EXPECT_EQ(kModeId60, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, findClosestKnownFrameRate) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+        const auto knownFrameRate = selector.findClosestKnownFrameRate(Fps::fromValue(fps));
+        const Fps expectedFrameRate = [fps] {
+            if (fps < 26.91f) return 24_Hz;
+            if (fps < 37.51f) return 30_Hz;
+            if (fps < 52.51f) return 45_Hz;
+            if (fps < 66.01f) return 60_Hz;
+            if (fps < 81.01f) return 72_Hz;
+            return 90_Hz;
+        }();
+
+        EXPECT_EQ(expectedFrameRate, knownFrameRate);
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_KnownFrameRate) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    struct Expectation {
+        Fps fps;
+        ftl::NonNull<DisplayModePtr> mode;
+    };
+
+    const std::initializer_list<Expectation> knownFrameRatesExpectations = {
+            {24_Hz, kMode60}, {30_Hz, kMode60}, {45_Hz, kMode90},
+            {60_Hz, kMode60}, {72_Hz, kMode90}, {90_Hz, kMode90},
+    };
+
+    // Make sure the test tests all the known frame rate
+    const auto& knownFrameRates = selector.knownFrameRates();
+    const bool equal = std::equal(knownFrameRates.begin(), knownFrameRates.end(),
+                                  knownFrameRatesExpectations.begin(),
+                                  [](Fps fps, const Expectation& expected) {
+                                      return isApproxEqual(fps, expected.fps);
+                                  });
+    EXPECT_TRUE(equal);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::Heuristic;
+
+    for (const auto& [fps, mode] : knownFrameRatesExpectations) {
+        layer.desiredRefreshRate = fps;
+        EXPECT_EQ(mode, selector.getBestFrameRateMode(layers));
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+    auto& explicitExactLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+    explicitExactLayer.name = "ExplicitExact";
+    explicitExactLayer.desiredRefreshRate = 30_Hz;
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
+
+    } else {
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
+    }
+
+    explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+    explicitExactLayer.desiredRefreshRate = 60_Hz;
+
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+    } else {
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+    }
+
+    explicitExactLayer.desiredRefreshRate = 72_Hz;
+    EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    explicitExactLayer.desiredRefreshRate = 90_Hz;
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    explicitExactLayer.desiredRefreshRate = 120_Hz;
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    using GlobalSignals = RefreshRateSelector::GlobalSignals;
+    const auto args = std::make_pair(std::vector<LayerRequirement>{},
+                                     GlobalSignals{.touch = true, .idle = true});
+
+    const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
+                                                                  {90_Hz, kMode90}}},
+                                                          GlobalSignals{.touch = true}};
+
+    selector.mutableGetRankedRefreshRatesCache() = {args, result};
+
+    EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
+
+    EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+    RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+
+    const auto result = selector.getRankedFrameRates(layers, globalSignals);
+
+    const auto& cache = selector.mutableGetRankedRefreshRatesCache();
+    ASSERT_TRUE(cache);
+
+    EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
+    EXPECT_EQ(cache->result, result);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExactTouchBoost) {
+    auto selector = createSelector(kModes_60_120, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+    auto& explicitExactLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+    explicitExactLayer.vote = LayerVoteType::ExplicitExact;
+    explicitExactLayer.name = "ExplicitExact";
+    explicitExactLayer.desiredRefreshRate = 30_Hz;
+
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+    } else {
+        EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers, {.touch = true}));
+    }
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
+
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers, {.touch = true}));
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_FractionalRefreshRates_ExactAndDefault) {
+    auto selector = createSelector(kModes_24_25_30_50_60_Frac, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
+    auto& explicitDefaultLayer = layers[0];
+    auto& explicitExactOrMultipleLayer = layers[1];
+
+    explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+    explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+    explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+    explicitDefaultLayer.name = "ExplicitDefault";
+    explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
+
+    EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers));
+}
+
+// b/190578904
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+    if (g_noSlowTests) {
+        GTEST_SKIP();
+    }
+
+    const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
+    constexpr int kMaxRefreshRate = 240;
+
+    DisplayModes displayModes;
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        const DisplayModeId modeId(fps);
+        displayModes.try_emplace(modeId,
+                                 createDisplayMode(modeId,
+                                                   Fps::fromValue(static_cast<float>(fps))));
+    }
+
+    const auto selector = createSelector(std::move(displayModes), DisplayModeId(kMinRefreshRate));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+        layers[0].desiredRefreshRate = fps;
+        layers[0].vote = vote;
+        EXPECT_EQ(fps.getIntValue(), selector.getBestFrameRateMode(layers)->getFps().getIntValue())
+                << "Failed for " << ftl::enum_string(vote);
+    };
+
+    for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+        const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+        testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+        testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+    }
+}
+
+// b/190578904
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_conflictingVotes) {
+    constexpr DisplayModeId kActiveModeId{0};
+    DisplayModes displayModes = makeModes(createDisplayMode(kActiveModeId, 43_Hz),
+                                          createDisplayMode(DisplayModeId(1), 53_Hz),
+                                          createDisplayMode(DisplayModeId(2), 55_Hz),
+                                          createDisplayMode(DisplayModeId(3), 60_Hz));
+
+    const RefreshRateSelector::GlobalSignals globalSignals = {.touch = false, .idle = false};
+    const auto selector = createSelector(std::move(displayModes), kActiveModeId);
+
+    const std::vector<LayerRequirement> layers = {
+            {
+                    .vote = LayerVoteType::ExplicitDefault,
+                    .desiredRefreshRate = 43_Hz,
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+            {
+                    .vote = LayerVoteType::ExplicitExactOrMultiple,
+                    .desiredRefreshRate = 53_Hz,
+                    .seamlessness = Seamlessness::SeamedAndSeamless,
+                    .weight = 0.41f,
+            },
+    };
+
+    EXPECT_EQ(53_Hz, selector.getBestFrameRateMode(layers, globalSignals)->getFps());
+}
+
+TEST_P(RefreshRateSelectorTest, modeComparison) {
+    EXPECT_LT(kMode60->getFps(), kMode90->getFps());
+    EXPECT_GE(kMode60->getFps(), kMode60->getFps());
+    EXPECT_GE(kMode90->getFps(), kMode90->getFps());
+}
+
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerAction) {
+    using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
+
+    auto selector = createSelector(kModes_60_90, kModeId90);
+
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+}
+
+TEST_P(RefreshRateSelectorTest, testKernelIdleTimerActionFor120Hz) {
+    using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
+
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 120_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, selector.getIdleTimerAction());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateDivisor) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId30);
+
+    const auto frameRate = 30_Hz;
+    Fps displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+    selector.setActiveMode(kModeId60, 60_Hz);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+    selector.setActiveMode(kModeId72, 72_Hz);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+    selector.setActiveMode(kModeId120, 120_Hz);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
+
+    selector.setActiveMode(kModeId90, 90_Hz);
+    displayRefreshRate = selector.getActiveMode().getFps();
+    EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
+
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 25_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(24_Hz, 23.976_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(30_Hz, 29.97_Hz));
+    EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(60_Hz, 59.94_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, isFractionalPairOrMultiple) {
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
+
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
+
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
+
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
+
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
+    EXPECT_TRUE(RefreshRateSelector::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
+
+    const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
+    for (auto refreshRate : refreshRates) {
+        EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(refreshRate, refreshRate));
+    }
+
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(24_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
+    EXPECT_FALSE(RefreshRateSelector::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_noLayers) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    EXPECT_TRUE(selector.getFrameRateOverrides({}, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_NonExplicit) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 60_Hz;
+
+    layers[0].vote = LayerVoteType::NoVote;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Min;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Max;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::Heuristic;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_Disabled) {
+    if (GetParam() != Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 60_Hz;
+
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    EXPECT_TRUE(selector.getFrameRateOverrides(layers, 120_Hz, {}).empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_60on120) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 60_Hz;
+
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+                                            {.ownerUid = 5678, .weight = 1.f}};
+
+    layers[0].name = "Test layer 1234";
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    layers[1].name = "Test layer 5678";
+    layers[1].desiredRefreshRate = 30_Hz;
+    layers[1].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_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));
+
+    layers[1].vote = LayerVoteType::Heuristic;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    layers[1].ownerUid = 1234;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_touch) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {.touch = true});
+    EXPECT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_DivisorIsNotDisplayRefreshRate) {
+    if (GetParam() == Config::FrameRateOverride::Disabled) {
+        return;
+    }
+
+    ASSERT_TRUE(GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ||
+                GetParam() == Config::FrameRateOverride::AppOverride ||
+                GetParam() == Config::FrameRateOverride::Enabled);
+
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 30_Hz;
+
+    const auto expetedFps =
+            GetParam() == Config::FrameRateOverride::AppOverrideNativeRefreshRates ? 60_Hz : 30_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+
+    layers[0].vote = LayerVoteType::ExplicitExact;
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    ASSERT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(expetedFps, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateInvalidPolicy) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    // The render frame rate cannot be greater than the physical refresh rate
+    {
+        const FpsRange physical = {60_Hz, 60_Hz};
+        const FpsRange render = {60_Hz, 120_Hz};
+        EXPECT_EQ(SetPolicyResult::Invalid,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRateRestrictsPhysicalRefreshRate) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    {
+        const FpsRange physical = {0_Hz, 120_Hz};
+        const FpsRange render = {0_Hz, 60_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+        const auto expectedMaxMode =
+                GetParam() == Config::FrameRateOverride::Enabled ? kMode120 : kMode60;
+        EXPECT_EQ(expectedMaxMode, selector.getMaxRefreshRateByPolicy());
+        EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+    }
+
+    {
+        const FpsRange physical = {0_Hz, 120_Hz};
+        const FpsRange render = {120_Hz, 120_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId60, {physical, render}, {physical, render}}));
+        EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+        EXPECT_EQ(kMode120, selector.getMinRefreshRateByPolicy());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_InPolicy) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    {
+        const FpsRange physical = {120_Hz, 120_Hz};
+        const FpsRange render = {60_Hz, 90_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId120, {physical, render}, {physical, render}}));
+    }
+
+    layers[0].name = "30Hz";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 30_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    auto frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    EXPECT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(60_Hz, frameRateOverrides.at(1234));
+
+    {
+        const FpsRange physical = {120_Hz, 120_Hz};
+        const FpsRange render = {30_Hz, 90_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId120, {physical, render}, {physical, render}}));
+    }
+
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    EXPECT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+
+    {
+        const FpsRange physical = {120_Hz, 120_Hz};
+        const FpsRange render = {30_Hz, 30_Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy(
+                          {kModeId120, {physical, render}, {physical, render}}));
+    }
+
+    layers[0].name = "60Hz";
+    layers[0].ownerUid = 1234;
+    layers[0].desiredRefreshRate = 60_Hz;
+    layers[0].vote = LayerVoteType::ExplicitDefault;
+
+    frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {});
+    EXPECT_EQ(1u, frameRateOverrides.size());
+    EXPECT_EQ(1u, frameRateOverrides.count(1234));
+    EXPECT_EQ(30_Hz, frameRateOverrides.at(1234));
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates) {
+    auto selector = createSelector(kModes_30_60_72_90_120, kModeId120);
+
+    // [renderRate, refreshRate]
+    const auto expected = []() -> std::vector<std::pair<Fps, Fps>> {
+        switch (GetParam()) {
+            case Config::FrameRateOverride::Disabled:
+            case Config::FrameRateOverride::AppOverrideNativeRefreshRates:
+            case Config::FrameRateOverride::AppOverride:
+                return {{30_Hz, 30_Hz},
+                        {60_Hz, 60_Hz},
+                        {72_Hz, 72_Hz},
+                        {90_Hz, 90_Hz},
+                        {120_Hz, 120_Hz}};
+            case Config::FrameRateOverride::Enabled:
+                return {{30_Hz, 30_Hz}, {36_Hz, 72_Hz}, {40_Hz, 120_Hz}, {45_Hz, 90_Hz},
+                        {60_Hz, 60_Hz}, {72_Hz, 72_Hz}, {90_Hz, 90_Hz},  {120_Hz, 120_Hz}};
+        }
+    }();
+
+    const auto& primaryRefreshRates = selector.getPrimaryFrameRates();
+    ASSERT_EQ(expected.size(), primaryRefreshRates.size());
+
+    for (size_t i = 0; i < expected.size(); i++) {
+        const auto [expectedRenderRate, expectedRefreshRate] = expected[i];
+        EXPECT_EQ(expectedRenderRate, primaryRefreshRates[i].fps);
+        EXPECT_EQ(expectedRefreshRate, primaryRefreshRates[i].modePtr->getFps());
+    }
+}
+
+TEST_P(RefreshRateSelectorTest, refreshRateIsCappedWithRenderFrameRate) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_60_120, kModeId60);
+
+    constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz};
+    constexpr FpsRange k0_60Hz = {0_Hz, 60_Hz};
+
+    constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
+                                       /*render*/ k0_120Hz};
+
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+                                        /*render*/ k0_120Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
+                                        /*render*/ k0_60Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+
+    {
+        constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
+                                        /*render*/ k0_60Hz};
+        EXPECT_EQ(SetPolicyResult::Changed,
+                  selector.setDisplayManagerPolicy({/*defaultMode*/ kModeId60,
+                                                    /*primaryRanges*/
+                                                    kPrimary,
+                                                    /*appRequestRanges*/
+                                                    kAppRequest}));
+    }
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    auto& layer = layers[0];
+
+    const auto expectedRenderRate =
+            GetParam() == Config::FrameRateOverride::Enabled ? 30_Hz : 60_Hz;
+
+    layer.name = "30Hz ExplicitDefault";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    layer.name = "30Hz Heuristic";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::Heuristic;
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    layer.name = "30Hz ExplicitExactOrMultiple";
+    layer.desiredRefreshRate = 30_Hz;
+    layer.vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
+    auto selector = createSelector(kModes_35_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+
+    const auto getIdleDisplayModeId = [&](LayerVoteType voteType,
+                                          bool touchActive) -> DisplayModeId {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = 90_Hz;
+
+        const auto [ranking, signals] =
+                selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
+
+        // Refresh rate will be chosen by either touch state or idle state.
+        EXPECT_EQ(!touchActive, signals.idle);
+        return ranking.front().frameRateMode.modePtr->getId();
+    };
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({kModeId60, {0_Hz, 90_Hz}}));
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
+
+    // Idle should be higher precedence than other layer frame rate considerations.
+    selector.setActiveMode(kModeId90, 90_Hz);
+    {
+        constexpr bool kTouchActive = false;
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Min, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Max, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::Heuristic, kTouchActive));
+        EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::ExplicitDefault, kTouchActive));
+        EXPECT_EQ(kModeId35,
+                  getIdleDisplayModeId(LayerVoteType::ExplicitExactOrMultiple, kTouchActive));
+    }
+
+    // Idle should be applied rather than the active mode when there are no layers.
+    EXPECT_EQ(kModeId35, selector.getBestFrameRateMode({}, {.idle = true})->getId());
+}
+
+TEST_P(RefreshRateSelectorTest, policyCanBeInfinity) {
+    auto selector = createSelector(kModes_60_120, kModeId120);
+
+    constexpr Fps inf = Fps::fromValue(std::numeric_limits<float>::infinity());
+
+    using namespace fps_approx_ops;
+    selector.setDisplayManagerPolicy({kModeId60, {0_Hz, inf}});
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(kMode120, selector.getMaxRefreshRateByPolicy());
+    EXPECT_EQ(kMode60, selector.getMinRefreshRateByPolicy());
+}
+
+TEST_P(RefreshRateSelectorTest, SupportsLowPhysicalRefreshRates) {
+    auto selector = createSelector(kModes_1_5_10, kModeId10);
+
+    EXPECT_EQ(kMode10, selector.getMaxRefreshRateByPolicy());
+    EXPECT_EQ(kMode1, selector.getMinRefreshRateByPolicy());
+}
+
+// TODO(b/266481656): Once this bug is fixed, we can remove this test
+TEST_P(RefreshRateSelectorTest, noLowerFrameRateOnMinVote) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].vote = LayerVoteType::Min;
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+
+    constexpr FpsRanges kCappedAt60 = {{30_Hz, 90_Hz}, {30_Hz, 60_Hz}};
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {DisplayModeId(kModeId60), kCappedAt60, kCappedAt60}));
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateIsCappedByPolicy) {
+    if (GetParam() != Config::FrameRateOverride::Enabled) {
+        return;
+    }
+
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    constexpr FpsRanges kCappedAt30 = {{60_Hz, 90_Hz}, {30_Hz, 30_Hz}};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy(
+                      {DisplayModeId(kModeId60), kCappedAt30, kCappedAt30}));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].vote = LayerVoteType::Min;
+    EXPECT_FRAME_RATE_MODE(kMode60, 30_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateNotInRange) {
+    auto selector = createSelector(kModes_60_90, kModeId60);
+
+    constexpr FpsRanges k60Only = {{60_Hz, 90_Hz}, {60_Hz, 60_Hz}};
+    constexpr FpsRanges kAll = {{0_Hz, 90_Hz}, {0_Hz, 90_Hz}};
+
+    EXPECT_EQ(SetPolicyResult::Changed,
+              selector.setDisplayManagerPolicy({DisplayModeId(kModeId60), k60Only, kAll}));
+
+    std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+    layers[0].name = "Test layer";
+    layers[0].vote = LayerVoteType::Heuristic;
+    layers[0].desiredRefreshRate = 45_Hz;
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 93c809e..965e378 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <ftl/fake_guard.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -21,7 +22,7 @@
 #include <mutex>
 
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
@@ -41,14 +42,13 @@
 using MockEventThread = android::mock::EventThread;
 using MockLayer = android::mock::MockLayer;
 
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
-
 class SchedulerTest : public testing::Test {
 protected:
     class MockEventThreadConnection : public android::EventThreadConnection {
     public:
         explicit MockEventThreadConnection(EventThread* eventThread)
-              : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
+              : EventThreadConnection(eventThread, /*callingUid*/ static_cast<uid_t>(0),
+                                      ResyncCallback()) {}
         ~MockEventThreadConnection() = default;
 
         MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
@@ -58,14 +58,31 @@
 
     SchedulerTest();
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(DisplayModeId(1), 120_Hz);
+    static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
+    static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
 
-    std::shared_ptr<RefreshRateConfigs> mConfigs =
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60), kMode60->getId());
+    static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
+    static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
+
+    static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz));
+    static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
+
+    std::shared_ptr<RefreshRateSelector> mSelector =
+            std::make_shared<RefreshRateSelector>(makeModes(kDisplay1Mode60),
+                                                  kDisplay1Mode60->getId());
 
     mock::SchedulerCallback mSchedulerCallback;
-    TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
+    TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback};
 
     ConnectionHandle mConnectionHandle;
     MockEventThread* mEventThread;
@@ -79,7 +96,7 @@
     mEventThread = eventThread.get();
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
 
-    mEventThreadConnection = new MockEventThreadConnection(mEventThread);
+    mEventThreadConnection = sp<MockEventThreadConnection>::make(mEventThread);
 
     // createConnection call to scheduler makes a createEventConnection call to EventThread. Make
     // sure that call gets executed and returns an EventThread::Connection object.
@@ -104,13 +121,7 @@
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false);
-
-    EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    mScheduler->onScreenAcquired(handle);
-
-    EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    mScheduler->onScreenReleased(handle);
+    mScheduler->onHotplugReceived(handle, kDisplayId1, false);
 
     std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
@@ -128,14 +139,8 @@
     ASSERT_EQ(mEventThreadConnection, connection);
     EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
-    EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
-    mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false);
-
-    EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
-    mScheduler->onScreenAcquired(mConnectionHandle);
-
-    EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
-    mScheduler->onScreenReleased(mConnectionHandle);
+    EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1);
+    mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false);
 
     std::string output("dump");
     EXPECT_CALL(*mEventThread, dump(output)).Times(1);
@@ -156,16 +161,17 @@
 
     // recordLayerHistory should be a noop
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
-    mScheduler->setDisplayPowerMode(kPowerModeOn);
+    FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
 
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
@@ -174,11 +180,14 @@
     sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     ASSERT_EQ(1u, mScheduler->layerHistorySize());
 
-    mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+    // Replace `mSelector` with a new `RefreshRateSelector` that has different display modes.
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
 }
 
@@ -192,14 +201,16 @@
 TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
     const auto mode = DisplayMode::Builder(hal::HWConfigId(0))
                               .setId(DisplayModeId(111))
-                              .setPhysicalDisplayId(PHYSICAL_DISPLAY_ID)
+                              .setPhysicalDisplayId(kDisplayId1)
                               .setVsyncPeriod(111111)
                               .build();
 
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
     ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+    EXPECT_NO_FATAL_FAILURE(
+            mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+                                                       {90_Hz, ftl::as_non_null(mode)}));
     EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
@@ -214,30 +225,184 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg->getFps(), 120_Hz);
+    return isApproxEqual(arg.front().mode.fps, 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
-    mScheduler->setRefreshRateConfigs(
-            std::make_shared<RefreshRateConfigs>(makeModes(kMode60, kMode120), kMode60->getId()));
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
     EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
 
-    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+                                   LayerHistory::LayerUpdateType::Buffer);
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
-    mScheduler->setDisplayPowerMode(kPowerModeOn);
+    FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn));
 
     constexpr uint32_t kDisplayArea = 999'999;
     mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
 
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(Is120Hz(), _)).Times(1);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(Is120Hz())).Times(1);
     mScheduler->chooseRefreshRateForContent();
 
     // No-op if layer requirements have not changed.
-    EXPECT_CALL(mSchedulerCallback, requestDisplayMode(_, _)).Times(0);
+    EXPECT_CALL(mSchedulerCallback, requestDisplayModes(_)).Times(0);
     mScheduler->chooseRefreshRateForContent();
 }
 
+TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
+
+    std::vector<RefreshRateSelector::LayerRequirement> layers =
+            std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+    mScheduler->setContentRequirements(layers);
+    GlobalSignals globalSignals = {.idle = true};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+    using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+
+    auto modeChoices = mScheduler->chooseDisplayModes();
+    ASSERT_EQ(1u, modeChoices.size());
+
+    auto choice = modeChoices.get(kDisplayId1);
+    ASSERT_TRUE(choice);
+    EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals));
+
+    globalSignals = {.idle = false};
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+    modeChoices = mScheduler->chooseDisplayModes();
+    ASSERT_EQ(1u, modeChoices.size());
+
+    choice = modeChoices.get(kDisplayId1);
+    ASSERT_TRUE(choice);
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
+
+    globalSignals = {.touch = true};
+    mScheduler->replaceTouchTimer(10);
+    mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+    modeChoices = mScheduler->chooseDisplayModes();
+    ASSERT_EQ(1u, modeChoices.size());
+
+    choice = modeChoices.get(kDisplayId1);
+    ASSERT_TRUE(choice);
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
+}
+
+TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
+    mScheduler->registerDisplay(kDisplayId1,
+                                std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+                                                                      kDisplay1Mode60->getId()));
+    mScheduler->registerDisplay(kDisplayId2,
+                                std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+                                                                      kDisplay2Mode60->getId()));
+
+    using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
+    TestableScheduler::DisplayModeChoiceMap expectedChoices;
+
+    {
+        const GlobalSignals globalSignals = {.idle = true};
+        expectedChoices =
+                ftl::init::map<const PhysicalDisplayId&,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{60_Hz, kDisplay1Mode60},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay2Mode60},
+                                                                 globalSignals);
+
+        std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
+                                                                     {.weight = 1.f}};
+        mScheduler->setContentRequirements(layers);
+        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+        const auto actualChoices = mScheduler->chooseDisplayModes();
+        EXPECT_EQ(expectedChoices, actualChoices);
+    }
+    {
+        const GlobalSignals globalSignals = {.idle = false};
+        expectedChoices =
+                ftl::init::map<const PhysicalDisplayId&,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
+                                                                 globalSignals);
+
+        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+        const auto actualChoices = mScheduler->chooseDisplayModes();
+        EXPECT_EQ(expectedChoices, actualChoices);
+    }
+    {
+        const GlobalSignals globalSignals = {.touch = true};
+        mScheduler->replaceTouchTimer(10);
+        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+        expectedChoices =
+                ftl::init::map<const PhysicalDisplayId&,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
+                                                                 globalSignals);
+
+        const auto actualChoices = mScheduler->chooseDisplayModes();
+        EXPECT_EQ(expectedChoices, actualChoices);
+    }
+    {
+        // The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
+        // Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
+        mScheduler
+                ->registerDisplay(kDisplayId3,
+                                  std::make_shared<RefreshRateSelector>(kDisplay3Modes,
+                                                                        kDisplay3Mode60->getId()));
+
+        const GlobalSignals globalSignals = {.touch = true};
+        mScheduler->replaceTouchTimer(10);
+        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+        expectedChoices = ftl::init::map<
+                const PhysicalDisplayId&,
+                DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
+                                   globalSignals)(kDisplayId2,
+                                                  FrameRateMode{120_Hz, kDisplay2Mode120},
+                                                  globalSignals)(kDisplayId3,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay3Mode60},
+                                                                 globalSignals);
+
+        const auto actualChoices = mScheduler->chooseDisplayModes();
+        EXPECT_EQ(expectedChoices, actualChoices);
+    }
+    {
+        // We should choose 60Hz despite the touch signal as pacesetter only supports 60Hz
+        mScheduler->setPacesetterDisplay(kDisplayId3);
+        const GlobalSignals globalSignals = {.touch = true};
+        mScheduler->replaceTouchTimer(10);
+        mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
+
+        expectedChoices = ftl::init::map<
+                const PhysicalDisplayId&,
+                DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
+                                   globalSignals)(kDisplayId2,
+                                                  FrameRateMode{60_Hz, kDisplay2Mode60},
+                                                  globalSignals)(kDisplayId3,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay3Mode60},
+                                                                 globalSignals);
+
+        const auto actualChoices = mScheduler->chooseDisplayModes();
+        EXPECT_EQ(expectedChoices, actualChoices);
+    }
+}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index b9a5f36..44ab569 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -24,8 +24,6 @@
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
-#include "BufferStateLayer.h"
-#include "EffectLayer.h"
 #include "Layer.h"
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
@@ -78,11 +76,11 @@
 }
 
 void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
-    layer.get()->addChild(child.get());
+    layer->addChild(child);
 }
 
 void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
-    layer.get()->removeChild(child.get());
+    layer->removeChild(child);
 }
 
 void SetFrameRateTest::commitTransaction() {
@@ -374,11 +372,6 @@
     const auto& layerFactory = GetParam();
 
     auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    if (!parent->isVisible()) {
-        // This is a hack as all the test layers except EffectLayer are not visible,
-        // but since the logic is unified in Layer, it should be fine.
-        return;
-    }
 
     auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
     addChild(parent, child);
@@ -387,11 +380,13 @@
     commitTransaction();
 
     auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
-    history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
-    history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+    history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
+                   LayerHistory::LayerUpdateType::Buffer);
+    history.record(child->getSequence(), child->getLayerProps(), 0, 0,
+                   LayerHistory::LayerUpdateType::Buffer);
 
-    const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
-    const auto summary = history.summarize(*configs, 0);
+    const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
+    const auto summary = history.summarize(*selectorPtr, 0);
 
     ASSERT_EQ(2u, summary.size());
     EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index e20818c..dbf0cd8 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -17,12 +17,60 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <scheduler/Fps.h>
+
 #include "DisplayTransactionTestHelpers.h"
+#include "FpsOps.h"
 
 namespace android {
 namespace {
 
-class CreateDisplayTest : public DisplayTransactionTest {};
+class CreateDisplayTest : public DisplayTransactionTest {
+public:
+    void createDisplayWithRequestedRefreshRate(const String8& name, uint64_t displayId,
+                                               float pacesetterDisplayRefreshRate,
+                                               float requestedRefreshRate,
+                                               float expectedAdjustedRefreshRate) {
+        // --------------------------------------------------------------------
+        // Call Expectations
+
+        // --------------------------------------------------------------------
+        // Invocation
+
+        sp<IBinder> displayToken = mFlinger.createDisplay(name, false, requestedRefreshRate);
+
+        // --------------------------------------------------------------------
+        // Postconditions
+
+        // The display should have been added to the current state
+        ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+        const auto& display = getCurrentDisplayState(displayToken);
+        EXPECT_TRUE(display.isVirtual());
+        EXPECT_EQ(display.requestedRefreshRate, Fps::fromValue(requestedRefreshRate));
+        EXPECT_EQ(name.string(), display.displayName);
+
+        std::optional<VirtualDisplayId> vid =
+                DisplayId::fromValue<VirtualDisplayId>(displayId | DisplayId::FLAG_VIRTUAL);
+        ASSERT_TRUE(vid.has_value());
+
+        sp<DisplayDevice> device =
+                mFlinger.createVirtualDisplayDevice(displayToken, *vid, requestedRefreshRate);
+        EXPECT_TRUE(device->isVirtual());
+        device->adjustRefreshRate(Fps::fromValue(pacesetterDisplayRefreshRate));
+        // verifying desired value
+        EXPECT_EQ(device->getAdjustedRefreshRate(), Fps::fromValue(expectedAdjustedRefreshRate));
+        // verifying rounding up
+        if (requestedRefreshRate < pacesetterDisplayRefreshRate) {
+            EXPECT_GE(device->getAdjustedRefreshRate(), Fps::fromValue(requestedRefreshRate));
+        } else {
+            EXPECT_EQ(device->getAdjustedRefreshRate(),
+                      Fps::fromValue(pacesetterDisplayRefreshRate));
+        }
+
+        // --------------------------------------------------------------------
+        // Cleanup conditions
+    }
+};
 
 TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
     const String8 name("virtual.test");
@@ -30,9 +78,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
 
@@ -61,9 +106,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
     // --------------------------------------------------------------------
     // Invocation
     int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
@@ -90,5 +132,82 @@
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 }
 
+// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRate0) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 0.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting negative refresh rate, will be ignored, same as requesting 0
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNegative) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = -60.f;
+    const float kExpectedAdjustedRefreshRate = 0.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a higher refresh rate than the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateHigh) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 90.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting the same refresh rate as the pacesetter
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateSame) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 60.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a divisor (30) of the pacesetter (60) should be honored
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 60.f;
+    const float kRequestedRefreshRate = 30.f;
+    const float kExpectedAdjustedRefreshRate = 30.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (45) of the pacesetter (120) should round up to a divisor (60)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisor) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 45.f;
+    const float kExpectedAdjustedRefreshRate = 60.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
+// Requesting a non divisor (75) of the pacesetter (120) should round up to pacesetter (120)
+TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisorMax) {
+    const String8 displayName("virtual.test");
+    const uint64_t displayId = 123ull;
+    const float kPacesetterDisplayRefreshRate = 120.f;
+    const float kRequestedRefreshRate = 75.f;
+    const float kExpectedAdjustedRefreshRate = 120.f;
+    createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+                                          kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index c7e61c9..93a3811 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -37,9 +37,6 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // The call should notify the interceptor that a display was created.
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     // Destroying the display commits a display transaction.
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
@@ -65,7 +62,7 @@
     // --------------------------------------------------------------------
     // Preconditions
 
-    sp<BBinder> displayToken = new BBinder();
+    sp<BBinder> displayToken = sp<BBinder>::make();
 
     // --------------------------------------------------------------------
     // Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b58add8..e176546 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -19,7 +19,9 @@
 
 #include "DisplayTransactionTestHelpers.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockDisplayModeSpecs.h"
 
+#include <ftl/fake_guard.h>
 #include <scheduler/Fps.h>
 
 namespace android {
@@ -40,14 +42,17 @@
         PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
         PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
 
+        auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(kModes, kModeId60);
+
+        setupScheduler(selectorPtr);
+
         mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+        mFlinger.configureAndCommit();
 
         mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
-                           .setDisplayModes(kModes, kModeId60)
+                           .setRefreshRateSelector(std::move(selectorPtr))
                            .inject();
 
-        setupScheduler(mDisplay->holdRefreshRateConfigs());
-
         // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
         // will call setActiveConfig instead of setActiveConfigWithConstraints.
         ON_CALL(*mComposer, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
@@ -55,7 +60,7 @@
     }
 
 protected:
-    void setupScheduler(std::shared_ptr<scheduler::RefreshRateConfigs>);
+    void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
 
     sp<DisplayDevice> mDisplay;
     mock::EventThread* mAppEventThread;
@@ -77,23 +82,25 @@
 };
 
 void DisplayModeSwitchingTest::setupScheduler(
-        std::shared_ptr<scheduler::RefreshRateConfigs> configs) {
+        std::shared_ptr<scheduler::RefreshRateSelector> selectorPtr) {
     auto eventThread = std::make_unique<mock::EventThread>();
     mAppEventThread = eventThread.get();
     auto sfEventThread = std::make_unique<mock::EventThread>();
 
     EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
+            .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+                                                             mock::EventThread::kCallingUid,
+                                                             ResyncCallback())));
 
     EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
     EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
+            .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+                                                             mock::EventThread::kCallingUid,
+                                                             ResyncCallback())));
 
     auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+    auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
 
     EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
     EXPECT_CALL(*vsyncTracker, currentPeriod())
@@ -102,22 +109,25 @@
     EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
     mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
                             std::move(eventThread), std::move(sfEventThread),
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            std::move(configs));
+                            std::move(selectorPtr),
+                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -130,30 +140,34 @@
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+    EXPECT_CALL(*mAppEventThread,
+                onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
     mFlinger.commit();
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        true, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -163,25 +177,29 @@
                                                hal::HWConfigId(kModeId90.value()), _, _))
             .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
 
-    EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+    EXPECT_CALL(*mAppEventThread,
+                onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
     // is still being processed the later call will be respected.
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+                                                                     120));
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
@@ -191,11 +209,12 @@
 
     mFlinger.commit();
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(),
-                                        false, 0.f, 180.f, 0.f, 180.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId120.value(), false, 0,
+                                                                     180));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -205,26 +224,29 @@
     mFlinger.commit();
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+    ftl::FakeGuard guard(kMainThreadContext);
+
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
-    mFlinger.onActiveDisplayChanged(mDisplay);
+    mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -259,7 +281,43 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode()->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
+}
+
+MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
+    if (!arg->getDesiredActiveMode()) {
+        *result_listener << "No desired active mode";
+        return false;
+    }
+
+    if (arg->getDesiredActiveMode()->modeOpt->modePtr->getId() != modeId) {
+        *result_listener << "Unexpected desired active mode " << modeId;
+        return false;
+    }
+
+    if (!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
+        *result_listener << "VsyncModulator did not shift to early phase";
+        return false;
+    }
+
+    return true;
+}
+
+MATCHER_P(ModeSettledTo, modeId, "") {
+    if (const auto desiredOpt = arg->getDesiredActiveMode()) {
+        *result_listener << "Unsettled desired active mode "
+                         << desiredOpt->modeOpt->modePtr->getId();
+        return false;
+    }
+
+    ftl::FakeGuard guard(kMainThreadContext);
+
+    if (arg->getActiveMode().modePtr->getId() != modeId) {
+        *result_listener << "Settled to unexpected active mode " << modeId;
+        return false;
+    }
+
+    return true;
 }
 
 TEST_F(DisplayModeSwitchingTest, multiDisplay) {
@@ -285,30 +343,26 @@
 
     const auto& innerDisplay = mDisplay;
 
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
-    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId60);
-    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+    mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
 
-    mFlinger.onActiveDisplayChanged(innerDisplay);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
-                                                  kModeId90.value(), false, 0.f, 120.f, 0.f,
-                                                  120.f));
+                                                  mock::createDisplayModeSpecs(kModeId90.value(),
+                                                                               false, 0.f, 120.f)));
 
     EXPECT_EQ(NO_ERROR,
               mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
-                                                  kModeId60.value(), false, 0.f, 120.f, 0.f,
-                                                  120.f));
+                                                  mock::createDisplayModeSpecs(kModeId60.value(),
+                                                                               false, 0.f, 120.f)));
 
-    // Transition on the inner display.
-    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
@@ -318,31 +372,18 @@
 
     mFlinger.commit();
 
-    // Transition on the inner display.
-    ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+    EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
     mFlinger.commit();
 
-    // Transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
 
-    // No transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId120);
+    mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
 
-    mFlinger.onActiveDisplayChanged(outerDisplay);
-
-    // No transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-
-    // Transition on the outer display.
-    ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(kOuterDisplayHwcId,
@@ -351,22 +392,13 @@
 
     mFlinger.commit();
 
-    // No transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-
-    // Transition on the outer display.
-    ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getDesiredActiveMode()->mode->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
 
     mFlinger.commit();
 
-    // No transition on the inner display.
-    EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(innerDisplay->getActiveMode()->getId(), kModeId90);
-
-    // Transition on the outer display.
-    EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
-    EXPECT_EQ(outerDisplay->getActiveMode()->getId(), kModeId60);
+    EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+    EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index 9ac2907..94d517a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -90,15 +90,12 @@
     Case::HdrSupport::setupComposerCallExpectations(this);
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
     expectHotplugReceived<Case, true>(mEventThread);
     expectHotplugReceived<Case, true>(mSFEventThread);
 }
 
 template <typename Case>
 void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
-    EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
     expectHotplugReceived<Case, false>(mEventThread);
     expectHotplugReceived<Case, false>(mSFEventThread);
 }
@@ -118,9 +115,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {.id = *displayId,
-                            .type = *connectionType,
-                            .hwcDisplayId = *hwcDisplayId};
+        expectedPhysical = {.id = *displayId, .hwcDisplayId = *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
@@ -145,10 +140,13 @@
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-    const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-    ASSERT_TRUE(displayTokenOpt);
+    const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+    ASSERT_TRUE(displayOpt);
 
-    verifyDisplayIsConnected<Case>(displayTokenOpt->get());
+    const auto& display = displayOpt->get();
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, display.snapshot().connectionType());
+
+    verifyDisplayIsConnected<Case>(display.token());
 }
 
 void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
@@ -175,7 +173,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -204,7 +202,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -239,7 +237,7 @@
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+    mFlinger.configureAndCommit();
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -247,10 +245,10 @@
     // HWComposer should not have an entry for the display
     EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-    // SF should not have a display token.
+    // SF should not have a PhysicalDisplay.
     const auto displayId = Case::Display::DISPLAY_ID::get();
     ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-    ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+    ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
 
     // The existing token should have been removed.
     verifyDisplayIsNotConnected(existing.token());
@@ -321,7 +319,7 @@
                 // --------------------------------------------------------------------
                 // Invocation
 
-                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+                mFlinger.configureAndCommit();
 
                 // --------------------------------------------------------------------
                 // Postconditions
@@ -329,10 +327,10 @@
                 // HWComposer should not have an entry for the display
                 EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
 
-                // SF should not have a display token.
+                // SF should not have a PhysicalDisplay.
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
-                ASSERT_FALSE(mFlinger.mutablePhysicalDisplayTokens().contains(displayId));
+                ASSERT_FALSE(mFlinger.mutablePhysicalDisplays().contains(displayId));
             }(),
             testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
 }
@@ -366,7 +364,7 @@
                 // --------------------------------------------------------------------
                 // Invocation
 
-                mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
+                mFlinger.configureAndCommit();
 
                 // --------------------------------------------------------------------
                 // Postconditions
@@ -376,9 +374,9 @@
                 const auto displayId = Case::Display::DISPLAY_ID::get();
                 ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
 
-                const auto displayTokenOpt = mFlinger.mutablePhysicalDisplayTokens().get(displayId);
-                ASSERT_TRUE(displayTokenOpt);
-                EXPECT_NE(existing.token(), displayTokenOpt->get());
+                const auto displayOpt = mFlinger.mutablePhysicalDisplays().get(displayId);
+                ASSERT_TRUE(displayOpt);
+                EXPECT_NE(existing.token(), displayOpt->get().token());
 
                 // A new display should be connected in its place.
                 verifyPhysicalDisplayIsConnected<Case>();
@@ -408,12 +406,12 @@
 
     // A virtual display was added to the current state, and it has a
     // surface(producer)
-    sp<BBinder> displayToken = new BBinder();
+    sp<BBinder> displayToken = sp<BBinder>::make();
 
     DisplayDeviceState state;
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
 
-    sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
+    sp<mock::GraphicBufferProducer> surface{sp<mock::GraphicBufferProducer>::make()};
     state.surface = surface;
     mFlinger.mutableCurrentState().displays.add(displayToken, state);
 
@@ -479,7 +477,7 @@
 
     // A virtual display was added to the current state, but it does not have a
     // surface.
-    sp<BBinder> displayToken = new BBinder();
+    sp<BBinder> displayToken = sp<BBinder>::make();
 
     DisplayDeviceState state;
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -656,9 +654,11 @@
     // Preconditions
 
     // A display is set up
-    auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new compositionengine::mock::DisplaySurface();
-    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto nativeWindow = sp<mock::NativeWindow>::make();
+    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
+    sp<GraphicBuffer> buf =
+
+            sp<GraphicBuffer>::make();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
@@ -701,9 +701,9 @@
     // Preconditions
 
     // A display is set up
-    auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new compositionengine::mock::DisplaySurface();
-    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto nativeWindow = sp<mock::NativeWindow>::make();
+    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
+    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
@@ -750,9 +750,9 @@
     // Preconditions
 
     // A display is set up
-    auto nativeWindow = new mock::NativeWindow();
-    auto displaySurface = new compositionengine::mock::DisplaySurface();
-    sp<GraphicBuffer> buf = new GraphicBuffer();
+    auto nativeWindow = sp<mock::NativeWindow>::make();
+    auto displaySurface = sp<compositionengine::mock::DisplaySurface>::make();
+    sp<GraphicBuffer> buf = sp<GraphicBuffer>::make();
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.setNativeWindow(nativeWindow);
     display.setDisplaySurface(displaySurface);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+    void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+        DisplayModes modes;
+        for (DisplayModePtr displayMode : displayModePtrs) {
+            modes.try_emplace(displayMode->getId(), displayMode);
+        }
+
+        mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+                           .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+                           .inject();
+        mDisplay->overrideHdrTypes(types);
+    }
+
+protected:
+    sp<DisplayDevice> mDisplay;
+
+    static constexpr DisplayModeId modeId1080p60{0};
+    static constexpr DisplayModeId modeId4k30{1};
+    static constexpr DisplayModeId modeId4k60{2};
+
+    static inline const DisplayModePtr mode1080p60 =
+            createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+    static inline const DisplayModePtr mode4k30 =
+            createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+    static inline const DisplayModePtr mode4k30NonStandard =
+            createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+    static inline const DisplayModePtr mode4k60 =
+            createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+    const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+                                        ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+    injectDisplayModes({mode4k60});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+    ASSERT_EQ(1, displayModes.size());
+    ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+                            displayModes[0].supportedHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+    ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+    injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+    ASSERT_EQ(2, displayModes.size());
+    for (size_t i = 0; i < displayModes.size(); i++) {
+        ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+                                displayModes[i].supportedHdrTypes.end(),
+                                [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+        ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+                                displayModes[i].supportedHdrTypes.end(),
+                                [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+        ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+    }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+    injectDisplayModes({mode4k60});
+    ui::DynamicDisplayInfo info;
+    mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+    std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+    ASSERT_EQ(2, displayHdrTypes.size());
+    ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+    ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+                            [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
index 0171f1b..5951c98 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
@@ -95,7 +95,7 @@
 }
 
 TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
-    sp<BBinder> notInternalDisplayToken = new BBinder();
+    sp<BBinder> notInternalDisplayToken = sp<BBinder>::make();
 
     ui::DisplayPrimaries primaries;
     populateDummyDisplayNativePrimaries(primaries);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
new file mode 100644
index 0000000..29acfaa
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SurfaceFlingerGetDisplayStatsTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DisplayStatInfo.h>
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockTimeStats.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace {
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+
+class SurfaceFlingerGetDisplayStatsTest : public Test {
+public:
+    void SetUp() override;
+
+protected:
+    TestableSurfaceFlinger mFlinger;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    sp<DisplayDevice> mDisplay;
+    sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
+            sp<compositionengine::mock::DisplaySurface>::make();
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
+    mock::TimeStats* mTimeStats = new mock::TimeStats();
+    Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
+    Hwc2::mock::Composer* mComposer = nullptr;
+};
+
+void SurfaceFlingerGetDisplayStatsTest::SetUp() {
+    mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
+    mComposer = new Hwc2::mock::Composer();
+    mPowerAdvisor = new Hwc2::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));
+    static constexpr bool kIsPrimary = true;
+    FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
+            .setPowerMode(hal::PowerMode::ON)
+            .inject(&mFlinger, mComposer);
+    auto compostionEngineDisplayArgs =
+            compositionengine::DisplayCreationArgsBuilder()
+                    .setId(DEFAULT_DISPLAY_ID)
+                    .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                    .setPowerAdvisor(mPowerAdvisor)
+                    .setName("injected display")
+                    .build();
+    auto compositionDisplay =
+            compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
+                                                   std::move(compostionEngineDisplayArgs));
+    mDisplay =
+            FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+                                      ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary)
+                    .setDisplaySurface(mDisplaySurface)
+                    .setNativeWindow(mNativeWindow)
+                    .setPowerMode(hal::PowerMode::ON)
+                    .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+                    .skipRegisterDisplay()
+                    .inject();
+}
+
+// TODO (b/277364366): Clients should be updated to pass in the display they want.
+TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) {
+    DisplayStatInfo info;
+    status_t status = mFlinger.getDisplayStats(nullptr, &info);
+    EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, explicitToken) {
+    DisplayStatInfo info;
+    status_t status = mFlinger.getDisplayStats(mDisplay->getDisplayToken().promote(), &info);
+    EXPECT_EQ(status, NO_ERROR);
+}
+
+TEST_F(SurfaceFlingerGetDisplayStatsTest, invalidToken) {
+    const String8 displayName("fakeDisplay");
+    sp<IBinder> displayToken = mFlinger.createDisplay(displayName, false);
+    DisplayStatInfo info;
+    status_t status = mFlinger.getDisplayStats(displayToken, &info);
+    EXPECT_EQ(status, NAME_NOT_FOUND);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
new file mode 100644
index 0000000..a2c54ac
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+#include <gui/AidlStatusUtil.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+
+using aidl::android::hardware::graphics::common::HdrConversionCapability;
+using aidl::android::hardware::graphics::common::HdrConversionStrategy;
+using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
+using gui::aidl_utils::statusTFromBinderStatus;
+
+TEST(HdrOutputControlTest, testGetHdrOutputConversionSupport) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status status = sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+}
+
+TEST(HdrOutputControlTest, testGetHdrConversionCapabilities) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status getSupportStatus =
+            sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+    std::vector<gui::HdrConversionCapability> capabilities;
+    binder::Status status = sf->getHdrConversionCapabilities(&capabilities);
+
+    if (hdrOutputConversionSupport) {
+        ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+    } else {
+        ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+    }
+}
+
+TEST(HdrOutputControlTest, testSetHdrConversionStrategy) {
+    sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+
+    bool hdrOutputConversionSupport;
+    binder::Status getSupportStatus =
+            sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
+    ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
+
+    std::vector<HdrConversionStrategy> strategies =
+            {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::passthrough)>),
+             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
+             HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
+                                           GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+    int32_t outPreferredHdrOutputType = 0;
+
+    for (HdrConversionStrategy strategy : strategies) {
+        binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+
+        if (hdrOutputConversionSupport) {
+            ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
+        } else {
+            ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status));
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index c9a2b00..1210d0b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -20,41 +20,18 @@
 #include "DisplayTransactionTestHelpers.h"
 
 namespace android {
-namespace {
 
 class HotplugTest : public DisplayTransactionTest {};
 
-TEST_F(HotplugTest, enqueuesEventsForDisplayTransaction) {
+TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) {
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2);
+
     constexpr HWDisplayId hwcDisplayId1 = 456;
-    constexpr HWDisplayId hwcDisplayId2 = 654;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the main thread id so that the current thread does not appear to be
-    // the main thread.
-    mFlinger.mutableMainThreadId() = std::thread::id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect a scheduled commit for the display transaction.
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate two hotplug events (a connect and a disconnect)
     mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED);
+
+    constexpr HWDisplayId hwcDisplayId2 = 654;
     mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED);
 
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // All events should be in the pending event queue.
     const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
     ASSERT_EQ(2u, pendingEvents.size());
     EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
@@ -63,49 +40,83 @@
     EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
 }
 
-TEST_F(HotplugTest, processesEnqueuedEventsIfCalledOnMainThread) {
-    constexpr HWDisplayId displayId1 = 456;
-
-    // --------------------------------------------------------------------
-    // Note:
-    // --------------------------------------------------------------------
-    // This test case is a bit tricky. We want to verify that
-    // onComposerHalHotplug() calls processDisplayHotplugEventsLocked(), but we
-    // don't really want to provide coverage for everything the later function
-    // does as there are specific tests for it.
-    // --------------------------------------------------------------------
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Set the main thread id so that the current thread does appear to be the
-    // main thread.
-    mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
-    // --------------------------------------------------------------------
-    // Call Expectations
-
-    // We expect a scheduled commit for the display transaction.
+TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
-    // --------------------------------------------------------------------
-    // Invocation
-
-    // Simulate a disconnect on a display id that is not connected. This should
-    // be enqueued by onComposerHalHotplug(), and dequeued by
-    // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+    constexpr HWDisplayId displayId1 = 456;
     mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED);
+    mFlinger.configure();
 
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The display transaction needed flag should be set.
-    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // There should be no event queued on return, as it should have been
-    // processed.
+    // The configure stage should consume the hotplug queue and produce a display transaction.
     EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+    EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
 }
 
-} // namespace
+TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
+    // Inject a primary display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+
+    using ExternalDisplay = ExternalDisplayVariant;
+    ExternalDisplay::setupHwcHotplugCallExpectations(this);
+    ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    // A single commit should be scheduled for both configure calls.
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+    mFlinger.configure();
+
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+
+    // Disconnecting a display that was already disconnected should be a no-op.
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    mFlinger.configure();
+
+    // The display should be scheduled for removal during the next commit. At this point, it should
+    // still exist but be marked as disconnected.
+    EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+    EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
+}
+
+TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
+    // Inject a primary display.
+    PrimaryDisplayVariant::injectHwcDisplay(this);
+
+    using ExternalDisplay = ExternalDisplayVariant;
+    constexpr bool kFailedHotplug = true;
+    ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
+
+    // Simulate a connect event that fails to load display modes due to HWC already having
+    // disconnected the display but SF yet having to process the queued disconnect event.
+    EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
+            .WillRepeatedly(Return(Error::BAD_DISPLAY));
+
+    // TODO(b/241286146): Remove this unnecessary call.
+    EXPECT_CALL(*mComposer,
+                setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+            .WillOnce(Return(Error::NONE));
+
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
+    mFlinger.configure();
+
+    // The hotplug should be rejected, so no HWComposer::DisplayData should be created.
+    EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+
+    // Disconnecting a display that does not exist should be a no-op.
+    ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+    mFlinger.configure();
+
+    EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
similarity index 71%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index 37cf05e..fc5f2b0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -22,9 +22,9 @@
 namespace android {
 namespace {
 
-class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+class InitializeDisplaysTest : public DisplayTransactionTest {};
 
-TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) {
     using Case = SimplePrimaryDisplayCase;
 
     // --------------------------------------------------------------------
@@ -38,23 +38,21 @@
     // --------------------------------------------------------------------
     // Call Expectations
 
-    // We expect the surface interceptor to possibly be used, but we treat it as
-    // disabled since it is called as a side effect rather than directly by this
-    // function.
-    EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
     // We expect a call to get the active display config.
     Case::Display::setupHwcGetActiveConfigCallExpectations(this);
 
     // We expect a scheduled commit for the display transaction.
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
-    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                        mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                nextAnticipatedVSyncTimeFrom(_))
+            .WillRepeatedly(Return(0));
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.onInitializeDisplays();
+    FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays());
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -78,19 +76,8 @@
     auto displayDevice = primaryDisplay.mutableDisplayDevice();
     EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
 
-    // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
-    FrameStats stats;
-    mFlinger.getAnimFrameTracker().getStats(&stats);
-    EXPECT_EQ(DEFAULT_VSYNC_PERIOD, stats.refreshPeriodNano);
-
     // The display transaction needed flag should be set.
     EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
-    // The compositor timing should be set to default values
-    const auto& compositorTiming = mFlinger.getCompositorTiming();
-    EXPECT_EQ(-DEFAULT_VSYNC_PERIOD, compositorTiming.deadline);
-    EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.interval);
-    EXPECT_EQ(DEFAULT_VSYNC_PERIOD, compositorTiming.presentLatency);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
new file mode 100644
index 0000000..e38f56e
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+struct MultiDisplayPacesetterTest : DisplayTransactionTest {
+    static constexpr bool kWithMockScheduler = false;
+    MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {}
+};
+
+TEST_F(MultiDisplayPacesetterTest, foldable) {
+    injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get());
+
+    // Inject inner and outer displays with uninitialized power modes.
+    sp<DisplayDevice> innerDisplay, outerDisplay;
+    constexpr bool kInitPowerMode = false;
+    {
+        InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+        auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this);
+        injector.setPowerMode(std::nullopt);
+        injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector());
+        innerDisplay = injector.inject();
+    }
+    {
+        OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this);
+        auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this);
+        injector.setPowerMode(std::nullopt);
+        outerDisplay = injector.inject();
+    }
+
+    // When the device boots, the inner display should be the pacesetter.
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+    // ...and should still be after powering on.
+    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+    // The outer display should become the pacesetter after folding.
+    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
+
+    // The inner display should become the pacesetter after unfolding.
+    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
+    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+    // The inner display should stay the pacesetter if both are powered on.
+    // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId());
+
+    // The outer display should become the pacesetter if designated.
+    mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId());
+    ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index ec7e8a7..4e9f293 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -21,6 +21,7 @@
 #include <thread>
 
 #include "DisplayTransactionTestHelpers.h"
+#include "FakeDisplayInjector.h"
 
 #include <android/hardware/power/Boost.h>
 
@@ -32,6 +33,8 @@
 TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
     using namespace std::chrono_literals;
 
+    injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
+
     mFlinger.scheduler()->replaceTouchTimer(100);
     std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
     EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index 2c9888d..d0290ea 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -28,9 +28,7 @@
 #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/system/window/MockNativeWindow.h"
 
 using namespace android;
@@ -53,28 +51,25 @@
 public:
     void SetUp() override;
 
-    void setupScheduler();
-
 protected:
     TestableSurfaceFlinger mFlinger;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
     sp<DisplayDevice> mDisplay;
     sp<compositionengine::mock::DisplaySurface> mDisplaySurface =
-            new compositionengine::mock::DisplaySurface();
-    mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
+            sp<compositionengine::mock::DisplaySurface>::make();
+    sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make();
     mock::TimeStats* mTimeStats = new mock::TimeStats();
     Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr;
     Hwc2::mock::Composer* mComposer = nullptr;
 };
 
 void SurfaceFlingerPowerHintTest::SetUp() {
-    setupScheduler();
+    mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID});
     mComposer = new Hwc2::mock::Composer();
     mPowerAdvisor = new Hwc2::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.setPowerHintSessionMode(true, true);
     mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor));
     static constexpr bool kIsPrimary = true;
     FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary)
@@ -96,57 +91,28 @@
                     .setDisplaySurface(mDisplaySurface)
                     .setNativeWindow(mNativeWindow)
                     .setPowerMode(hal::PowerMode::ON)
+                    .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
+                    .skipRegisterDisplay()
                     .inject();
-    mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken();
-}
-
-void SurfaceFlingerPowerHintTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread),
-                            TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp,
-                            TestableSurfaceFlinger::kTwoDisplayModes);
 }
 
 TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) {
     ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true));
 
-    const std::chrono::nanoseconds mockVsyncPeriod = 15ms;
-    EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(1);
-
-    const nsecs_t now = systemTime();
-    const std::chrono::nanoseconds mockHwcRunTime = 20ms;
+    EXPECT_CALL(*mPowerAdvisor, updateTargetWorkDuration(_)).Times(1);
     EXPECT_CALL(*mDisplaySurface,
                 prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
             .Times(1);
-    EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
-            .WillOnce([mockHwcRunTime] {
-                std::this_thread::sleep_for(mockHwcRunTime);
-                return hardware::graphics::composer::V2_1::Error::NONE;
-            });
-    EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1);
-    static constexpr bool kVsyncId = 123; // arbitrary
-    mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
+    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;
+    });
+    EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(1);
+
+    const TimePoint frameTime = scheduler::SchedulerClock::now();
+    constexpr Period kMockVsyncPeriod = 15ms;
+    mFlinger.commitAndComposite(frameTime, VsyncId{123}, frameTime + kMockVsyncPeriod);
 }
 
 TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) {
@@ -154,22 +120,20 @@
 
     mDisplay->setPowerMode(hal::PowerMode::DOZE);
 
-    const std::chrono::nanoseconds mockVsyncPeriod = 15ms;
-    EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0);
-
-    const nsecs_t now = systemTime();
-    const std::chrono::nanoseconds mockHwcRunTime = 20ms;
+    EXPECT_CALL(*mPowerAdvisor, updateTargetWorkDuration(_)).Times(0);
     EXPECT_CALL(*mDisplaySurface,
                 prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
             .Times(1);
-    EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
-            .WillOnce([mockHwcRunTime] {
-                std::this_thread::sleep_for(mockHwcRunTime);
-                return hardware::graphics::composer::V2_1::Error::NONE;
-            });
-    EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0);
-    static constexpr bool kVsyncId = 123; // arbitrary
-    mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count());
+    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;
+    });
+    EXPECT_CALL(*mPowerAdvisor, reportActualWorkDuration()).Times(0);
+
+    const TimePoint frameTime = scheduler::SchedulerClock::now();
+    constexpr Period kMockVsyncPeriod = 15ms;
+    mFlinger.commitAndComposite(frameTime, VsyncId{123}, frameTime + kMockVsyncPeriod);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
index 7d9e22b..9c7f55b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -34,7 +34,7 @@
     // Preconditions
 
     // We have an unknown display token not associated with a known display
-    sp<BBinder> displayToken = new BBinder();
+    sp<BBinder> displayToken = sp<BBinder>::make();
 
     // The requested display state references the unknown display.
     DisplayState state;
@@ -95,7 +95,7 @@
     display.inject();
 
     // There is a surface that can be set.
-    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+    sp<mock::GraphicBufferProducer> surface = sp<mock::GraphicBufferProducer>::make();
 
     // The current display state has the surface set
     display.mutableCurrentDisplayState().surface = surface;
@@ -132,7 +132,7 @@
     display.inject();
 
     // There is a surface that can be set.
-    sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+    sp<mock::GraphicBufferProducer> surface = sp<mock::GraphicBufferProducer>::make();
 
     // The current display state does not have a surface
     display.mutableCurrentDisplayState().surface = nullptr;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index b560025..7754c21 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -59,54 +59,45 @@
 };
 
 struct EventThreadBaseSupportedVariant {
-    static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
-        // The callback should not be notified to toggle VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_)).Times(0);
-
-        // The event thread should not be notified.
-        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
-        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+    static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) {
+        // Expect no change to hardware nor synthetic VSYNC.
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, _)).Times(0);
+        EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
     }
 };
 
 struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
-    static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // These calls are only expected for the primary display.
-
-        // Instead expect no calls.
-        setupVsyncAndEventThreadNoCallExpectations(test);
+    static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+        setupVsyncNoCallExpectations(test);
     }
 
-    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // These calls are only expected for the primary display.
-
-        // Instead expect no calls.
-        setupVsyncAndEventThreadNoCallExpectations(test);
+    static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+        setupVsyncNoCallExpectations(test);
     }
 };
 
 struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
-    static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // The callback should be notified to enable VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(true)).Times(1);
-
-        // The event thread should be notified that the screen was acquired.
-        EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+    static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+        // Expect to enable hardware VSYNC and disable synthetic VSYNC.
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, true)).Times(1);
+        EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1);
     }
 
-    static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
-        // The callback should be notified to disable VSYNC.
-        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(false)).Times(1);
-
-        // The event thread should not be notified that the screen was released.
-        EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+    static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+        // Expect to disable hardware VSYNC and enable synthetic VSYNC.
+        EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, false)).Times(1);
+        EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1);
     }
 };
 
 struct DispSyncIsSupportedVariant {
     static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_VSYNC_PERIOD)).Times(1);
-        EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+        auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule();
+        EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()),
+                    startPeriodTransition(DEFAULT_VSYNC_PERIOD, false))
+                .Times(1);
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel())
+                .Times(1);
     }
 };
 
@@ -133,14 +124,13 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::EventThread::setupEnableVsyncCallExpectations(test);
         Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
     static void verifyPostconditions(DisplayTransactionTest* test) {
         EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
     }
 };
 
@@ -149,20 +139,19 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
-        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncNoCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
     static void verifyPostconditions(DisplayTransactionTest* test) {
         EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
-        EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
     }
 };
 
 struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::EventThread::setupDisableVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -175,7 +164,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
     }
 
@@ -187,7 +176,7 @@
 struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -196,7 +185,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::EventThread::setupEnableVsyncCallExpectations(test);
         Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
@@ -205,7 +194,7 @@
 struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncNoCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
@@ -214,7 +203,7 @@
       : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+        Case::EventThread::setupEnableVsyncCallExpectations(test);
         Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
@@ -224,7 +213,7 @@
       : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+        Case::EventThread::setupDisableVsyncCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
     }
 };
@@ -233,7 +222,7 @@
       : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
-        Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+        Case::EventThread::setupVsyncNoCallExpectations(test);
         Case::setupNoComposerPowerModeCallExpectations(test);
     }
 };
@@ -255,34 +244,24 @@
     using DispSync = DispSyncVariant;
     using Transition = TransitionVariant;
 
-    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+    static sp<DisplayDevice> injectDisplayWithInitialPowerMode(DisplayTransactionTest* test,
+                                                               PowerMode mode) {
         Display::injectHwcDisplayWithNoDefaultCapabilities(test);
-        auto display = Display::makeFakeExistingDisplayInjector(test);
-        display.inject();
-        display.mutableDisplayDevice()->setPowerMode(mode);
-        if (display.mutableDisplayDevice()->isInternal()) {
-            test->mFlinger.mutableActiveDisplayToken() =
-                    display.mutableDisplayDevice()->getDisplayToken();
-        }
-
+        auto injector = Display::makeFakeExistingDisplayInjector(test);
+        const auto display = injector.inject();
+        display->setPowerMode(mode);
         return display;
     }
 
-    static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
-        test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
+    static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, PhysicalDisplayId id,
+                                         bool enabled) {
+        test->mFlinger.scheduler()->setInitialHwVsyncEnabled(id, enabled);
     }
 
     static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
     }
 
-    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
-                                                        PowerMode mode) {
-        EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
-        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
-                .Times(1);
-    }
-
     static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
         // Any calls to get the active config will return a default value.
         EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
@@ -323,9 +302,6 @@
 public:
     template <typename Case>
     void transitionDisplayCommon();
-
-    template <bool kBoot>
-    sp<DisplayDevice> activeDisplayTest();
 };
 
 template <PowerMode PowerMode>
@@ -345,21 +321,22 @@
     Case::Doze::setupComposerCallExpectations(this);
     auto display =
             Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
-    Case::setInitialPrimaryHWVsyncEnabled(this,
-                                          PowerModeInitialVSyncEnabled<
-                                                  Case::Transition::INITIAL_POWER_MODE>::value);
+    auto displayId = display->getId();
+    if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) {
+        Case::setInitialHwVsyncEnabled(this, *physicalDisplayId,
+                                       PowerModeInitialVSyncEnabled<
+                                               Case::Transition::INITIAL_POWER_MODE>::value);
+    }
 
     // --------------------------------------------------------------------
     // Call Expectations
 
-    Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
     Case::Transition::template setupCallExpectations<Case>(this);
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
-                                  Case::Transition::TARGET_POWER_MODE);
+    mFlinger.setPowerModeInternal(display, Case::Transition::TARGET_POWER_MODE);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -502,87 +479,5 @@
     transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
 }
 
-template <bool kBoot>
-sp<DisplayDevice> SetPowerModeInternalTest::activeDisplayTest() {
-    using Case = SimplePrimaryDisplayCase;
-
-    // --------------------------------------------------------------------
-    // Preconditions
-
-    // Inject a primary display.
-    Case::Display::injectHwcDisplay(this);
-    auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
-    injector.setPowerMode(kBoot ? std::nullopt : std::make_optional(PowerMode::OFF));
-
-    const auto display = injector.inject();
-    EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
-
-    using PowerCase = PrimaryDisplayPowerCase<TransitionOffToOnVariant>;
-    TransitionOffToOnVariant::template setupCallExpectations<PowerCase>(this);
-
-    constexpr size_t kTimes = kBoot ? 1 : 0;
-    EXPECT_CALL(*mRenderEngine, onActiveDisplaySizeChanged(display->getSize())).Times(kTimes);
-    EXPECT_CALL(*mEventThread, onModeChanged(display->getActiveMode())).Times(kTimes);
-
-    if constexpr (kBoot) {
-        mFlinger.mutableActiveDisplayToken() = nullptr;
-    }
-
-    // --------------------------------------------------------------------
-    // Invocation
-
-    mFlinger.setPowerModeInternal(display, PowerMode::ON);
-
-    // --------------------------------------------------------------------
-    // Postconditions
-
-    // The primary display should be the active display.
-    EXPECT_EQ(display->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
-
-    Mock::VerifyAndClearExpectations(mComposer);
-    Mock::VerifyAndClearExpectations(mRenderEngine);
-    Mock::VerifyAndClearExpectations(mEventThread);
-    Mock::VerifyAndClearExpectations(mVsyncController);
-    Mock::VerifyAndClearExpectations(mVSyncTracker);
-    Mock::VerifyAndClearExpectations(mFlinger.scheduler());
-    Mock::VerifyAndClearExpectations(&mFlinger.mockSchedulerCallback());
-
-    return display;
-}
-
-TEST_F(SetPowerModeInternalTest, activeDisplayBoot) {
-    constexpr bool kBoot = true;
-    activeDisplayTest<kBoot>();
-}
-
-TEST_F(SetPowerModeInternalTest, activeDisplaySingle) {
-    constexpr bool kBoot = false;
-    activeDisplayTest<kBoot>();
-}
-
-TEST_F(SetPowerModeInternalTest, activeDisplayDual) {
-    constexpr bool kBoot = false;
-    const auto innerDisplay = activeDisplayTest<kBoot>();
-
-    // Inject a powered-off outer display.
-    const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
-            [&](FakeDisplayDeviceInjector& injector) { injector.setPowerMode(PowerMode::OFF); },
-            {.displayId = PhysicalDisplayId::fromPort(254u),
-             .hwcDisplayId = 1,
-             .isPrimary = false});
-
-    EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
-
-    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON);
-
-    EXPECT_EQ(outerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
-
-    mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF);
-    mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON);
-
-    EXPECT_EQ(innerDisplay->getDisplayToken(), mFlinger.mutableActiveDisplayToken());
-}
-
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index a0e078b..c0796df 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -17,6 +17,8 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <ftl/fake_guard.h>
+
 #include "DisplayHardware/DisplayMode.h"
 
 #include "DisplayTransactionTestHelpers.h"
@@ -34,16 +36,13 @@
     static constexpr bool WIDE_COLOR_SUPPORTED = true;
 
     static void injectConfigChange(DisplayTransactionTest* test) {
-        test->mFlinger.mutableHasWideColorDisplay() = true;
+        test->mFlinger.mutableSupportsWideColor() = true;
         test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
 
-        EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
-                                Return(Error::NONE)));
         EXPECT_CALL(*test->mComposer,
                     getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
                 .WillOnce(DoAll(SetArgPointee<2>(
@@ -197,10 +196,10 @@
 
 template <typename Case>
 void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
-    const sp<BBinder> displayToken = new BBinder();
+    const sp<BBinder> displayToken = sp<BBinder>::make();
     const sp<compositionengine::mock::DisplaySurface> displaySurface =
-            new compositionengine::mock::DisplaySurface();
-    const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
+            sp<compositionengine::mock::DisplaySurface>::make();
+    const auto producer = sp<mock::GraphicBufferProducer>::make();
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -246,11 +245,20 @@
                                             .setDpiY(DEFAULT_DPI)
                                             .setGroup(0)
                                             .build();
+
         state.physical = {.id = *displayId,
-                          .type = *connectionType,
                           .hwcDisplayId = *hwcDisplayId,
-                          .supportedModes = makeModes(activeMode),
-                          .activeMode = std::move(activeMode)};
+                          .activeMode = activeMode};
+
+        ui::ColorModes colorModes;
+        if constexpr (Case::WideColorSupport::WIDE_COLOR_SUPPORTED) {
+            colorModes.push_back(ColorMode::DISPLAY_P3);
+        }
+
+        mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
+                                                              *connectionType,
+                                                              makeModes(activeMode),
+                                                              std::move(colorModes), std::nullopt);
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -262,9 +270,8 @@
     // --------------------------------------------------------------------
     // Postconditions
 
-    ASSERT_TRUE(device != nullptr);
+    ASSERT_NE(nullptr, device);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
-    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
@@ -280,9 +287,8 @@
               device->receivesInput());
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
-        EXPECT_EQ(1, device->getSupportedModes().size());
-        EXPECT_NE(nullptr, device->getActiveMode());
-        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode()->getHwcId());
+        ftl::FakeGuard guard(kMainThreadContext);
+        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
new file mode 100644
index 0000000..0e5f1ea
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
@@ -0,0 +1,194 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+
+class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
+public:
+    SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); }
+
+protected:
+    sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
+        LayerCreationArgs args =
+                LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
+        inOutlayerMetadata = args.metadata;
+        return sp<Layer>::make(args);
+    }
+
+    TestableSurfaceFlinger mFlinger;
+};
+
+class LayerMetadataBuilder {
+public:
+    LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
+
+    LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
+        mLayerMetadata.setInt32(key, value);
+        return *this;
+    }
+
+    LayerMetadata build() { return mLayerMetadata; }
+
+private:
+    LayerMetadata mLayerMetadata;
+};
+
+bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
+    return lhs.mMap == rhs.mMap;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
+    stream << "LayerMetadata{";
+    for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
+        if (it != layerMetadata.mMap.cbegin()) {
+            stream << ", ";
+        }
+        stream << layerMetadata.itemToString(it->first, ":");
+    }
+    return stream << "}";
+}
+
+// Test that the snapshot's layer metadata is set.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
+    auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layer = createLayer("layer", layerMetadata);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
+}
+
+// Test that snapshot layer metadata is set by merging the child's metadata on top of its
+// parent's metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_OWNER_UID, 1)
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .build();
+    auto layerA = createLayer("parent", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
+    auto layerB = createLayer("child", layerBMetadata);
+    layerA->addChild(layerB);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
+    auto expectedChildMetadata =
+            LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
+    EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
+}
+
+// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
+// that parent's relative layer metadata.
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
+    auto layerA = createLayer("relative-parent", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
+    auto layerB = createLayer("relative-child", layerBMetadata);
+    layerB->setRelativeLayer(layerAHandle, 1);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
+// two other layers.
+//
+// Layer
+//      A
+//     / \
+//    B   D
+//   /
+//  C
+//
+// Z-order Relatives
+//    B <- D <- C
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerBMetadata = LayerMetadataBuilder()
+                                  .setInt32(METADATA_TASK_ID, 2)
+                                  .setInt32(METADATA_OWNER_PID, 3)
+                                  .build();
+    auto layerB = createLayer("layer-b", layerBMetadata);
+    auto layerBHandle = layerB->getHandle();
+    LayerMetadata layerCMetadata;
+    auto layerC = createLayer("layer-c", layerCMetadata);
+    auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
+    auto layerD = createLayer("layer-d", layerDMetadata);
+    auto layerDHandle = layerD->getHandle();
+    layerB->addChild(layerC);
+    layerA->addChild(layerB);
+    layerA->addChild(layerD);
+    layerC->setRelativeLayer(layerDHandle, 1);
+    layerD->setRelativeLayer(layerBHandle, 1);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    auto expectedLayerDRelativeMetadata =
+            LayerMetadataBuilder()
+                    // From layer A, parent of relative parent
+                    .setInt32(METADATA_OWNER_UID, 1)
+                    // From layer B, relative parent
+                    .setInt32(METADATA_TASK_ID, 2)
+                    .setInt32(METADATA_OWNER_PID, 3)
+                    // added by layer creation args
+                    .setInt32(gui::METADATA_CALLING_UID,
+                              layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
+                    .build();
+    EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
+    auto expectedLayerCRelativeMetadata =
+            LayerMetadataBuilder()
+                    // From layer A, parent of relative parent
+                    .setInt32(METADATA_OWNER_UID, 1)
+                    // From layer B, relative parent of relative parent
+                    .setInt32(METADATA_OWNER_PID, 3)
+                    // From layer D, relative parent
+                    .setInt32(METADATA_TASK_ID, 4)
+                    // added by layer creation args
+                    .setInt32(gui::METADATA_CALLING_UID,
+                              layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
+                    .build();
+    EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
+}
+
+TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
+       updatesRelativeMetadataMultipleRelativeChildren) {
+    auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
+    auto layerA = createLayer("layer-a", layerAMetadata);
+    auto layerAHandle = layerA->getHandle();
+    LayerMetadata layerBMetadata;
+    auto layerB = createLayer("layer-b", layerBMetadata);
+    LayerMetadata layerCMetadata;
+    auto layerC = createLayer("layer-c", layerCMetadata);
+    layerB->setRelativeLayer(layerAHandle, 1);
+    layerC->setRelativeLayer(layerAHandle, 2);
+    layerA->commitChildList();
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
+    mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
+
+    mFlinger.updateLayerMetadataSnapshot();
+
+    EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
+    EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+    EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 4708572..3b6a987 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -16,15 +16,18 @@
 
 #pragma once
 
-#include <Scheduler/Scheduler.h>
+#include <ftl/fake_guard.h>
 #include <gmock/gmock.h>
 #include <gui/ISurfaceComposer.h>
 
+#include <scheduler/interface/ICompositor.h>
+
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncDispatch.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
 
@@ -32,17 +35,18 @@
 
 class TestableScheduler : public Scheduler, private ICompositor {
 public:
-    TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+    TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
-                              std::make_unique<mock::VSyncTracker>(), std::move(configs),
-                              callback) {}
+                              std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr),
+                              /* modulatorPtr */ nullptr, callback) {}
 
     TestableScheduler(std::unique_ptr<VsyncController> controller,
-                      std::unique_ptr<VSyncTracker> tracker,
-                      std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
-          : Scheduler(*this, callback, Feature::kContentDetection) {
-        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
-        setRefreshRateConfigs(std::move(configs));
+                      std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
+                      sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+          : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
+        const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
+        registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
+                        std::move(tracker));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
             // Execute task to prevent broken promise exception on destruction.
@@ -50,6 +54,7 @@
         });
     }
 
+    MOCK_METHOD(void, scheduleConfigure, (), (override));
     MOCK_METHOD(void, scheduleFrame, (), (override));
     MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
 
@@ -58,21 +63,47 @@
         return Scheduler::createConnection(std::move(eventThread));
     }
 
-    /* ------------------------------------------------------------------------
-     * Read-write access to private data to set up preconditions and assert
-     * post-conditions.
-     */
-    auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
-    auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+    auto refreshRateSelector() { return pacesetterSelectorPtr(); }
 
+    void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+        registerDisplay(displayId, std::move(selectorPtr),
+                        std::make_unique<mock::VsyncController>(),
+                        std::make_shared<mock::VSyncTracker>());
+    }
+
+    void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+                         std::unique_ptr<VsyncController> controller,
+                         std::shared_ptr<VSyncTracker> tracker) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr),
+                                           std::shared_ptr<VsyncSchedule>(
+                                                   new VsyncSchedule(displayId, std::move(tracker),
+                                                                     std::make_shared<
+                                                                             mock::VSyncDispatch>(),
+                                                                     std::move(controller))));
+    }
+
+    void unregisterDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::unregisterDisplay(displayId);
+    }
+
+    std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+        return mPacesetterDisplayId;
+    }
+
+    void setPacesetterDisplay(PhysicalDisplayId displayId) {
+        ftl::FakeGuard guard(kMainThreadContext);
+        Scheduler::setPacesetterDisplay(displayId);
+    }
+
+    auto& mutableAppConnectionHandle() { return mAppConnectionHandle; }
     auto& mutableLayerHistory() { return mLayerHistory; }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
     }
 
-    auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
-
     size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
         return mLayerHistory.mActiveLayerInfos.size();
     }
@@ -93,9 +124,27 @@
         return mPolicy.touch == Scheduler::TouchState::Active;
     }
 
+    void setTouchStateAndIdleTimerPolicy(GlobalSignals globalSignals) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.touch = globalSignals.touch ? TouchState::Active : TouchState::Inactive;
+        mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
+    }
+
+    void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
+        std::lock_guard<std::mutex> lock(mPolicyLock);
+        mPolicy.contentRequirements = std::move(layers);
+    }
+
+    using Scheduler::DisplayModeChoice;
+    using Scheduler::DisplayModeChoiceMap;
+
+    DisplayModeChoiceMap chooseDisplayModes() NO_THREAD_SAFETY_ANALYSIS {
+        return Scheduler::chooseDisplayModes();
+    }
+
     void dispatchCachedReportedMode() {
         std::lock_guard<std::mutex> lock(mPolicyLock);
-        return Scheduler::dispatchCachedReportedMode();
+        Scheduler::dispatchCachedReportedMode();
     }
 
     void clearCachedReportedMode() {
@@ -103,14 +152,22 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
-        return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
+        Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
+    }
+
+    void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
+        auto schedule = getVsyncSchedule(id);
+        std::lock_guard<std::mutex> lock(schedule->mHwVsyncLock);
+        schedule->mHwVsyncState = enabled ? VsyncSchedule::HwVsyncState::Enabled
+                                          : VsyncSchedule::HwVsyncState::Disabled;
     }
 
 private:
     // ICompositor overrides:
-    bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
-    void composite(nsecs_t, int64_t) override {}
+    void configure() override {}
+    bool commit(TimePoint, VsyncId, TimePoint) override { return false; }
+    void composite(TimePoint, VsyncId) override {}
     void sample() override {}
 };
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3cffec1..945e488 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -27,33 +27,37 @@
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
+#include <ftl/fake_guard.h>
+#include <ftl/match.h>
 #include <gui/ScreenCaptureResults.h>
 
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
-#include "ContainerLayer.h"
+#include <ui/DynamicDisplayInfo.h>
 #include "DisplayDevice.h"
-#include "EffectLayer.h"
 #include "FakeVsyncConfiguration.h"
 #include "FrameTracer/FrameTracer.h"
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerHandle.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
+#include "RenderArea.h"
 #include "Scheduler/MessageQueue.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/RefreshRateSelector.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
 #include "SurfaceFlingerDefaultFactory.h"
-#include "SurfaceInterceptor.h"
 #include "TestableScheduler.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"
 
 namespace android {
 
-class EventThread;
+struct DisplayStatInfo;
 
 namespace renderengine {
 
@@ -84,22 +88,18 @@
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
-    sp<SurfaceInterceptor> createSurfaceInterceptor() override {
-        return new android::impl::SurfaceInterceptor();
-    }
-
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
-        return new StartPropertySetThread(timestampPropertyValue);
+        return sp<StartPropertySetThread>::make(timestampPropertyValue);
     }
 
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override {
-        return new DisplayDevice(creationArgs);
+        return sp<DisplayDevice>::make(creationArgs);
     }
 
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
                                           uint32_t layerCount, uint64_t usage,
                                           std::string requestorName) override {
-        return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+        return sp<GraphicBuffer>::make(width, height, format, layerCount, usage, requestorName);
     }
 
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
@@ -112,18 +112,6 @@
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
-    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
-                                                       const sp<SurfaceFlinger>& flinger,
-                                                       const wp<Layer>& layer) override {
-        return new MonitoredProducer(producer, flinger, layer);
-    }
-
-    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
-                                                      renderengine::RenderEngine& renderEngine,
-                                                      uint32_t textureName, Layer* layer) override {
-        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
-    }
-
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>& producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -134,18 +122,12 @@
         return compositionengine::impl::createCompositionEngine();
     }
 
-    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
-        return nullptr;
-    }
+    sp<Layer> createBufferStateLayer(const LayerCreationArgs&) override { return nullptr; }
 
-    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
-        return nullptr;
-    }
+    sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
-    sp<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
-
-    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override {
-        return nullptr;
+    sp<LayerFE> createLayerFE(const std::string& layerName) override {
+        return sp<LayerFE>::make(layerName);
     }
 
     std::unique_ptr<FrameTracer> createFrameTracer() override {
@@ -173,6 +155,11 @@
     CreateCompositionEngineFunction mCreateCompositionEngine;
 };
 
+struct MockSchedulerOptions {
+    PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0);
+    bool useNiceMock = false;
+};
+
 } // namespace surfaceflinger::test
 
 class TestableSurfaceFlinger {
@@ -183,7 +170,6 @@
         if (!mFlinger) {
             mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization);
         }
-        mFlinger->mAnimationTransactionTimeout = ms2ns(10);
     }
 
     SurfaceFlinger* flinger() { return mFlinger.get(); }
@@ -193,7 +179,8 @@
     // functions.
 
     void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) {
-        mFlinger->mCompositionEngine->setRenderEngine(std::move(renderEngine));
+        mFlinger->mRenderEngine = std::move(renderEngine);
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
@@ -211,77 +198,109 @@
 
     enum class SchedulerCallbackImpl { kNoOp, kMock };
 
-    static constexpr struct OneDisplayMode {
-    } kOneDisplayMode;
+    struct DefaultDisplayMode {
+        // The ID of the injected RefreshRateSelector and its default display mode.
+        PhysicalDisplayId displayId;
+    };
 
-    static constexpr struct TwoDisplayModes {
-    } kTwoDisplayModes;
+    using RefreshRateSelectorPtr = scheduler::Scheduler::RefreshRateSelectorPtr;
 
-    using RefreshRateConfigsPtr = std::shared_ptr<scheduler::RefreshRateConfigs>;
-
-    using DisplayModesVariant =
-            std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateConfigsPtr>;
+    using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>;
 
     void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
-                        std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
+                        std::shared_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
+                        DisplayModesVariant modesVariant,
                         SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp,
-                        DisplayModesVariant modesVariant = kOneDisplayMode,
                         bool useNiceMock = false) {
-        RefreshRateConfigsPtr configs;
-        if (std::holds_alternative<RefreshRateConfigsPtr>(modesVariant)) {
-            configs = std::move(std::get<RefreshRateConfigsPtr>(modesVariant));
-        } else {
-            constexpr DisplayModeId kModeId60{0};
-            DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz));
+        RefreshRateSelectorPtr selectorPtr = ftl::match(
+                modesVariant,
+                [](DefaultDisplayMode arg) {
+                    constexpr DisplayModeId kModeId60{0};
+                    return std::make_shared<scheduler::RefreshRateSelector>(
+                            makeModes(mock::createDisplayMode(arg.displayId, kModeId60, 60_Hz)),
+                            kModeId60);
+                },
+                [](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; });
 
-            if (std::holds_alternative<TwoDisplayModes>(modesVariant)) {
-                constexpr DisplayModeId kModeId90{1};
-                modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz));
-            }
-
-            configs = std::make_shared<scheduler::RefreshRateConfigs>(modes, kModeId60);
-        }
-
-        const auto fps = configs->getActiveMode()->getFps();
+        const auto fps = selectorPtr->getActiveMode().fps;
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
-        mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
-                mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         mFlinger->mRefreshRateStats =
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
                                                               hal::PowerMode::OFF);
 
+        mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+
         using Callback = scheduler::ISchedulerCallback;
         Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp
                 ? static_cast<Callback&>(mNoOpSchedulerCallback)
                 : static_cast<Callback&>(mSchedulerCallback);
 
+        auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+                mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
         if (useNiceMock) {
             mScheduler =
                     new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
                                                                         std::move(vsyncTracker),
-                                                                        std::move(configs),
+                                                                        std::move(selectorPtr),
+                                                                        std::move(modulatorPtr),
                                                                         callback);
         } else {
             mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
                                                           std::move(vsyncTracker),
-                                                          std::move(configs), callback);
+                                                          std::move(selectorPtr),
+                                                          std::move(modulatorPtr), callback);
         }
 
-        mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+        mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms);
+
+        mScheduler->mutableAppConnectionHandle() =
+                mScheduler->createConnection(std::move(appEventThread));
+
+        mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle();
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
         resetScheduler(mScheduler);
     }
 
+    void setupMockScheduler(test::MockSchedulerOptions options = {}) {
+        using testing::_;
+        using testing::Return;
+
+        auto eventThread = makeMock<mock::EventThread>(options.useNiceMock);
+        auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock);
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
+                                                                 mock::EventThread::kCallingUid,
+                                                                 ResyncCallback())));
+
+        auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
+        auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
+
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread),
+                       std::move(sfEventThread), DefaultDisplayMode{options.displayId},
+                       SchedulerCallbackImpl::kNoOp, options.useNiceMock);
+    }
+
     void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
 
     scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
     scheduler::mock::SchedulerCallback& mockSchedulerCallback() { return mSchedulerCallback; }
 
-    auto& mutableVsyncModulator() { return mFlinger->mVsyncModulator; }
-
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFactory.mCreateBufferQueue = f;
@@ -310,7 +329,7 @@
                                        const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
-        layer->editCompositionState()->sidebandStream = sidebandStream;
+        layer->editLayerSnapshot()->sidebandStream = sidebandStream;
     }
 
     void setLayerCompositionType(const sp<Layer>& layer,
@@ -330,39 +349,39 @@
         layer->mDrawingParent = drawingParent;
     }
 
-    void setPowerHintSessionMode(bool early, bool late) {
-        mFlinger->mPowerHintSessionMode = {.late = late, .early = early};
-    }
-
     /* ------------------------------------------------------------------------
      * Forwarding for functions being tested
      */
 
-    nsecs_t commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVSyncTime) {
-        mFlinger->commit(frameTime, vsyncId, expectedVSyncTime);
+    void configure() { mFlinger->configure(); }
+
+    void configureAndCommit() {
+        configure();
+        commitTransactionsLocked(eDisplayTransactionNeeded);
+    }
+
+    TimePoint commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
+        mFlinger->commit(frameTime, vsyncId, expectedVsyncTime);
         return frameTime;
     }
 
-    nsecs_t commit(nsecs_t frameTime, int64_t vsyncId) {
-        std::chrono::nanoseconds period = 10ms;
-        return commit(frameTime, vsyncId, frameTime + period.count());
+    TimePoint commit(TimePoint frameTime, VsyncId vsyncId) {
+        return commit(frameTime, vsyncId, frameTime + Period(10ms));
     }
 
-    nsecs_t commit() {
-        const nsecs_t now = systemTime();
-        const nsecs_t expectedVsyncTime = now + 10'000'000;
-        return commit(now, kVsyncId, expectedVsyncTime);
+    TimePoint commit() {
+        const TimePoint frameTime = scheduler::SchedulerClock::now();
+        return commit(frameTime, kVsyncId);
     }
 
-    void commitAndComposite(const nsecs_t frameTime, const int64_t vsyncId,
-                            const nsecs_t expectedVsyncTime) {
-        mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), kVsyncId);
+    void commitAndComposite(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime) {
+        mFlinger->composite(commit(frameTime, vsyncId, expectedVsyncTime), vsyncId);
     }
 
     void commitAndComposite() { mFlinger->composite(commit(), kVsyncId); }
 
-    auto createDisplay(const String8& displayName, bool secure) {
-        return mFlinger->createDisplay(displayName, secure);
+    auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
+        return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
     }
 
     auto destroyDisplay(const sp<IBinder>& displayToken) {
@@ -386,9 +405,10 @@
                                                        dispSurface, producer);
     }
 
-    auto commitTransactionsLocked(uint32_t transactionFlags) {
+    void commitTransactionsLocked(uint32_t transactionFlags) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        return mFlinger->commitTransactionsLocked(transactionFlags);
+        ftl::FakeGuard guard(kMainThreadContext);
+        mFlinger->commitTransactionsLocked(transactionFlags);
     }
 
     void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
@@ -400,10 +420,7 @@
         return mFlinger->setDisplayStateLocked(s);
     }
 
-    // Allow reading display state without locking, as if called on the SF main thread.
-    auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
-        return mFlinger->onInitializeDisplays();
-    }
+    void initializeDisplays() FTL_FAKE_GUARD(kMainThreadContext) { mFlinger->initializeDisplays(); }
 
     auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
 
@@ -418,19 +435,22 @@
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto renderScreenImpl(const RenderArea& renderArea,
-                                SurfaceFlinger::TraverseLayersFunction traverseLayers,
-                                const std::shared_ptr<renderengine::ExternalTexture>& buffer,
-                                bool forSystem, bool regionSampling) {
+    auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
+                          SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
+                          const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+                          bool forSystem, bool regionSampling) {
         ScreenCaptureResults captureResults;
-        return mFlinger->renderScreenImpl(renderArea, traverseLayers, buffer, forSystem,
-                                                regionSampling, false /* grayscale */,
-                                                captureResults);
+        return FTL_FAKE_GUARD(kMainThreadContext,
+                              mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers,
+                                                         buffer, forSystem, regionSampling,
+                                                         false /* grayscale */, captureResults));
     }
 
     auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
+                                    std::unordered_set<uint32_t> excludeLayerIds,
                                     const LayerVector::Visitor& visitor) {
-        return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor);
+        return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid,
+                                                                    excludeLayerIds, visitor);
     }
 
     auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -438,106 +458,138 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
-    auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
-    auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
-    auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
-
-    auto setTransactionState(
-            const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
-            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
-            const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
-            bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
-            std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
-        return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
-                                             inputWindowCommands, desiredPresentTime,
-                                             isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
-                                             listenerCallbacks, transactionId);
+    auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
+    auto& getPendingTransactionQueue() {
+        return mFlinger->mTransactionHandler.mPendingTransactionQueues;
+    }
+    size_t getPendingTransactionCount() {
+        return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
     }
 
-    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
+    auto setTransactionState(
+            const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+            const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+            const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+            bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+            bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
+            uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
+        return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
+                                             inputWindowCommands, desiredPresentTime,
+                                             isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
+                                             listenerCallbacks, transactionId,
+                                             mergedTransactionIds);
+    }
+
+    auto setTransactionStateInternal(TransactionState& transaction) {
+        return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction));
+    }
+
+    auto flushTransactionQueues() {
+        return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId));
+    }
 
     auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
         return mFlinger->onTransact(code, data, reply, flags);
     }
 
-    auto getGPUContextPriority() { return mFlinger->getGPUContextPriority(); }
+    auto getGpuContextPriority() { return mFlinger->getGpuContextPriority(); }
 
     auto calculateMaxAcquiredBufferCount(Fps refreshRate,
                                          std::chrono::nanoseconds presentLatency) const {
         return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
     }
 
-    auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
-                                    bool allowGroupSwitching, float primaryRefreshRateMin,
-                                    float primaryRefreshRateMax, float appRequestRefreshRateMin,
-                                    float appRequestRefreshRateMax) {
-        return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
-                                                    primaryRefreshRateMin, primaryRefreshRateMax,
-                                                    appRequestRefreshRateMin,
-                                                    appRequestRefreshRateMax);
+    auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                    const gui::DisplayModeSpecs& specs) {
+        return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
     }
 
-    void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
+    void onActiveDisplayChanged(const DisplayDevice* inactiveDisplayPtr,
+                                const DisplayDevice& activeDisplay) {
         Mutex::Autolock lock(mFlinger->mStateLock);
-        mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+        ftl::FakeGuard guard(kMainThreadContext);
+        mFlinger->onActiveDisplayChangedLocked(inactiveDisplayPtr, activeDisplay);
     }
 
-    auto createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
-                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
-                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
-        return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, parentLayer,
-                                     outTransformHint);
+    auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
+                     gui::CreateSurfaceResult& outResult) {
+        args.parentHandle = parentHandle;
+        return mFlinger->createLayer(args, outResult);
     }
 
     auto mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
-                     sp<IBinder>* outHandle, int32_t* outLayerId) {
-        return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
+                     gui::CreateSurfaceResult& outResult) {
+        return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult);
+    }
+
+    void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+
+    void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+                                        ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+        mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+    }
+
+    sp<DisplayDevice> createVirtualDisplayDevice(const sp<IBinder> displayToken,
+                                                 VirtualDisplayId displayId,
+                                                 float requestedRefreshRate) {
+        constexpr ui::Size kResolution = {1080, 1920};
+        auto compositionDisplay = compositionengine::impl::
+                createDisplay(mFlinger->getCompositionEngine(),
+                              compositionengine::DisplayCreationArgsBuilder()
+                                      .setId(displayId)
+                                      .setPixels(kResolution)
+                                      .setPowerAdvisor(&mPowerAdvisor)
+                                      .build());
+        DisplayDeviceCreationArgs creationArgs(mFlinger, mFlinger->getHwComposer(), displayToken,
+                                               compositionDisplay);
+        creationArgs.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
+        creationArgs.nativeWindow = sp<mock::NativeWindow>::make();
+        return sp<DisplayDevice>::make(creationArgs);
+    }
+
+    status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* outInfo) {
+        return mFlinger->getDisplayStats(displayToken, outInfo);
     }
 
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
 
-    const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; }
-    const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; }
     const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; }
     auto& getHwComposer() const {
         return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
     }
     auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
 
-    const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
-
     mock::FrameTracer* getFrameTracer() const {
         return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
     }
 
-    nsecs_t getAnimationTransactionTimeout() const {
-        return mFlinger->mAnimationTransactionTimeout;
-    }
-
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
      */
 
     const auto& displays() const { return mFlinger->mDisplays; }
+    const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; }
     const auto& currentState() const { return mFlinger->mCurrentState; }
     const auto& drawingState() const { return mFlinger->mDrawingState; }
     const auto& transactionFlags() const { return mFlinger->mTransactionFlags; }
-    const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; }
 
-    auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
+    const auto& hwcPhysicalDisplayIdMap() const { return getHwComposer().mPhysicalDisplayIdMap; }
+    const auto& hwcDisplayData() const { return getHwComposer().mDisplayData; }
+
+    auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
 
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
     auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
     auto& mutableDisplays() { return mFlinger->mDisplays; }
+    auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
     auto& mutableDrawingState() { return mFlinger->mDrawingState; }
     auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
-    auto& mutableInterceptor() { return mFlinger->mInterceptor; }
+    auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
     auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
     auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
-    auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; }
     auto& mutableTexturePool() { return mFlinger->mTexturePool; }
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
@@ -546,12 +598,15 @@
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
-    auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
+    auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
+    auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; }
 
-    auto fromHandle(const sp<IBinder>& handle) {
-        return mFlinger->fromHandle(handle);
+    auto& mutableActiveDisplayRotationFlags() {
+        return SurfaceFlinger::sActiveDisplayRotationFlags;
     }
 
+    auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
+
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the SurfaceFlinger instance may
@@ -560,11 +615,10 @@
         mutableDisplays().clear();
         mutableCurrentState().displays.clear();
         mutableDrawingState().displays.clear();
-        mutableInterceptor().clear();
         mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
-        mFlinger->mCompositionEngine->setRenderEngine(
-                std::unique_ptr<renderengine::RenderEngine>());
+        mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
+        mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
     }
 
     /* ------------------------------------------------------------------------
@@ -638,7 +692,7 @@
             return *this;
         }
 
-        auto& setPowerMode(hal::PowerMode mode) {
+        auto& setPowerMode(std::optional<hal::PowerMode> mode) {
             mPowerMode = mode;
             return *this;
         }
@@ -660,16 +714,18 @@
             // is much longer lived.
             auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
                                                          mHwcDisplayType);
-
             display->mutableIsConnected() = true;
-            display->setPowerMode(mPowerMode);
+
+            if (mPowerMode) {
+                display->setPowerMode(*mPowerMode);
+            }
+
             flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
                     .WillRepeatedly(
                             DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
                                   Return(hal::Error::NONE)));
-
             EXPECT_CALL(*composer,
                         getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
                     .WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -728,7 +784,7 @@
         int32_t mDpiY = DEFAULT_DPI;
         int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
         hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
-        hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+        std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
         const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
                 mCapabilities = nullptr;
     };
@@ -740,16 +796,22 @@
                                   std::optional<ui::DisplayConnectionType> connectionType,
                                   std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
               : mFlinger(flinger),
-                mCreationArgs(flinger.mFlinger.get(), flinger.mFlinger->getHwComposer(),
-                              mDisplayToken, display),
+                mCreationArgs(flinger.mFlinger, flinger.mFlinger->getHwComposer(), mDisplayToken,
+                              display),
+                mConnectionType(connectionType),
                 mHwcDisplayId(hwcDisplayId) {
-            mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
             mCreationArgs.initialPowerMode = hal::PowerMode::ON;
         }
 
         sp<IBinder> token() const { return mDisplayToken; }
 
+        auto physicalDisplay() const {
+            return ftl::Optional(mCreationArgs.compositionDisplay->getDisplayId())
+                    .and_then(&PhysicalDisplayId::tryCast)
+                    .and_then(display::getPhysicalDisplay(mFlinger.physicalDisplays()));
+        }
+
         DisplayDeviceState& mutableDrawingDisplayState() {
             return mFlinger.mutableDrawingState().displays.editValueFor(mDisplayToken);
         }
@@ -770,16 +832,17 @@
             return mFlinger.mutableDisplays().get(mDisplayToken)->get();
         }
 
-        // If `configs` is nullptr, the injector creates RefreshRateConfigs from the `modes`.
-        // Otherwise, it uses `configs`, which the caller must create using the same `modes`.
-        //
-        // TODO(b/182939859): Once `modes` can be retrieved from RefreshRateConfigs, remove
-        // the `configs` parameter in favor of an alternative setRefreshRateConfigs API.
-        auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId,
-                              std::shared_ptr<scheduler::RefreshRateConfigs> configs = nullptr) {
-            mCreationArgs.supportedModes = std::move(modes);
+        auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
+            mDisplayModes = std::move(modes);
             mCreationArgs.activeModeId = activeModeId;
-            mCreationArgs.refreshRateConfigs = std::move(configs);
+            mCreationArgs.refreshRateSelector = nullptr;
+            return *this;
+        }
+
+        auto& setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
+            mDisplayModes = selectorPtr->displayModes();
+            mCreationArgs.activeModeId = selectorPtr->getActiveMode().modePtr->getId();
+            mCreationArgs.refreshRateSelector = std::move(selectorPtr);
             return *this;
         }
 
@@ -820,13 +883,18 @@
             return *this;
         }
 
+        auto& skipRegisterDisplay() {
+            mRegisterDisplay = false;
+            return *this;
+        }
+
         sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS {
             const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
 
-            auto& modes = mCreationArgs.supportedModes;
+            auto& modes = mDisplayModes;
             auto& activeModeId = mCreationArgs.activeModeId;
 
-            if (displayId && !mCreationArgs.refreshRateConfigs) {
+            if (displayId && !mCreationArgs.refreshRateSelector) {
                 if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
                     if (modes.empty()) {
                         constexpr DisplayModeId kModeId{0};
@@ -846,67 +914,87 @@
                         activeModeId = kModeId;
                     }
 
-                    mCreationArgs.refreshRateConfigs =
-                            std::make_shared<scheduler::RefreshRateConfigs>(modes, activeModeId);
+                    mCreationArgs.refreshRateSelector =
+                            std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
                 }
             }
 
+            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
+            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
+
             DisplayDeviceState state;
-            if (const auto type = mCreationArgs.connectionType) {
+            state.isSecure = mCreationArgs.isSecure;
+
+            if (mConnectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
-                const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
-                LOG_ALWAYS_FATAL_IF(!physicalId);
+                const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
+                LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
+                const auto physicalId = *physicalIdOpt;
+
+                if (mCreationArgs.isPrimary) {
+                    mFlinger.mutableActiveDisplayId() = physicalId;
+                }
+
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
+                const auto fps = activeMode->get()->getFps();
 
-                state.physical = {.id = *physicalId,
-                                  .type = *type,
+                state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
-                                  .deviceProductInfo = {},
-                                  .supportedModes = modes,
                                   .activeMode = activeMode->get()};
 
-                if (mCreationArgs.isPrimary) {
-                    mFlinger.mutableActiveDisplayToken() = mDisplayToken;
+                mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
+                                                                      physicalId, *mConnectionType,
+                                                                      std::move(modes),
+                                                                      ui::ColorModes(),
+                                                                      std::nullopt);
+
+                if (mFlinger.scheduler() && mRegisterDisplay) {
+                    mFlinger.scheduler()->registerDisplay(physicalId,
+                                                          display->holdRefreshRateSelector());
                 }
-            }
 
-            state.isSecure = mCreationArgs.isSecure;
-
-            sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
-            if (!display->isVirtual()) {
-                display->setActiveMode(activeModeId);
+                display->setActiveMode(activeModeId, fps, fps);
             }
-            mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (const auto& physical = state.physical) {
-                mFlinger.mutablePhysicalDisplayTokens().emplace_or_replace(physical->id,
-                                                                           mDisplayToken);
-            }
-
             return display;
         }
 
     private:
         TestableSurfaceFlinger& mFlinger;
-        sp<BBinder> mDisplayToken = new BBinder();
+        sp<BBinder> mDisplayToken = sp<BBinder>::make();
         DisplayDeviceCreationArgs mCreationArgs;
+        DisplayModes mDisplayModes;
+        bool mRegisterDisplay = true;
+        const std::optional<ui::DisplayConnectionType> mConnectionType;
         const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };
 
 private:
-    constexpr static int64_t kVsyncId = 123;
+    template <typename T>
+    static std::unique_ptr<T> makeMock(bool useNiceMock) {
+        return useNiceMock ? std::make_unique<testing::NiceMock<T>>() : std::make_unique<T>();
+    }
+
+    template <typename T>
+    static std::shared_ptr<T> makeSharedMock(bool useNiceMock) {
+        return useNiceMock ? std::make_shared<testing::NiceMock<T>>() : std::make_shared<T>();
+    }
+
+    static constexpr VsyncId kVsyncId{123};
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
+    std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 6ffc039..a9ae1d3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -44,11 +44,14 @@
 namespace {
 
 using testing::_;
+using testing::AllOf;
 using testing::AnyNumber;
 using testing::Contains;
+using testing::ElementsAre;
 using testing::HasSubstr;
 using testing::InSequence;
 using testing::Not;
+using testing::Property;
 using testing::SizeIs;
 using testing::StrEq;
 using testing::UnorderedElementsAre;
@@ -645,7 +648,7 @@
     ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
 
     ASSERT_EQ(1, globalProto.stats_size());
-    const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+    const SFTimeStatsLayerProto& layerProto = globalProto.stats(0);
     ASSERT_TRUE(layerProto.has_layer_name());
     EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
     ASSERT_TRUE(layerProto.has_total_frames());
@@ -653,7 +656,7 @@
     ASSERT_EQ(6, layerProto.deltas_size());
     for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
         ASSERT_EQ(1, deltaProto.histograms_size());
-        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+        const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0);
         EXPECT_EQ(1, histogramProto.frame_count());
         if ("post2acquire" == deltaProto.delta_name()) {
             EXPECT_EQ(1, histogramProto.time_millis());
@@ -673,6 +676,46 @@
     }
 }
 
+using LayerProto = SFTimeStatsLayerProto;
+using DeltaProto = SFTimeStatsDeltaProto;
+using BucketProto = SFTimeStatsHistogramBucketProto;
+
+TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta
+    // Slightly unstable frames
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_THAT(globalProto.stats(),
+                ElementsAre(AllOf(
+                        Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)),
+                        Property(&LayerProto::total_frames, 4),
+                        Property(&LayerProto::deltas,
+                                 Contains(AllOf(Property(&DeltaProto::delta_name,
+                                                         "present2presentDelta"),
+                                                Property(&DeltaProto::histograms,
+                                                         UnorderedElementsAre(
+                                                                 AllOf(Property(&BucketProto::
+                                                                                        time_millis,
+                                                                                0),
+                                                                       Property(&BucketProto::
+                                                                                        frame_count,
+                                                                                1)),
+                                                                 AllOf(Property(&BucketProto::
+                                                                                        time_millis,
+                                                                                1),
+                                                                       Property(&BucketProto::
+                                                                                        frame_count,
+                                                                                2))))))))));
+}
+
 TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -1099,8 +1142,10 @@
                                       kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
                                       DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1234,8 +1279,10 @@
                                       GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
                                       DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1320,16 +1367,19 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
     // The first time record is never uploaded to stats.
-    ASSERT_EQ(atomList.atom_size(), 3);
+    ASSERT_EQ(atomList.atom_size(), 4);
     // Layers are ordered based on the hash in LayerStatsKey. For this test, the order happens to
-    // be: 0 - Battery 1 - Performance 2 - Standard
+    // be: 0 - Battery 1 - Custom 2 - Performance 3 - Standard
     const SurfaceflingerStatsLayerInfo& atom0 = atomList.atom(0);
 
     EXPECT_EQ(atom0.layer_name(), genLayerName(LAYER_ID_0));
@@ -1364,7 +1414,7 @@
     EXPECT_EQ(atom1.uid(), UID_0);
     EXPECT_EQ(atom1.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
     EXPECT_EQ(atom1.render_rate_bucket(), RENDER_RATE_BUCKET_0);
-    EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+    EXPECT_EQ(atom1.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_CUSTOM);
 
     const SurfaceflingerStatsLayerInfo& atom2 = atomList.atom(2);
 
@@ -1377,12 +1427,30 @@
     EXPECT_THAT(atom2.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
     EXPECT_THAT(atom2.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
     EXPECT_THAT(atom2.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
-    EXPECT_EQ(atom2.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
-    EXPECT_EQ(atom2.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_EQ(atom2.late_acquire_frames(), 0);
+    EXPECT_EQ(atom2.bad_desired_present_frames(), 0);
     EXPECT_EQ(atom2.uid(), UID_0);
     EXPECT_EQ(atom2.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
     EXPECT_EQ(atom2.render_rate_bucket(), RENDER_RATE_BUCKET_0);
-    EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
+    EXPECT_EQ(atom2.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE);
+
+    const SurfaceflingerStatsLayerInfo& atom3 = atomList.atom(3);
+
+    EXPECT_EQ(atom3.layer_name(), genLayerName(LAYER_ID_0));
+    EXPECT_EQ(atom3.total_frames(), 1);
+    EXPECT_EQ(atom3.dropped_frames(), 0);
+    EXPECT_THAT(atom3.present_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_THAT(atom3.post_to_present(), HistogramEq(buildExpectedHistogram({4}, {1})));
+    EXPECT_THAT(atom3.acquire_to_present(), HistogramEq(buildExpectedHistogram({3}, {1})));
+    EXPECT_THAT(atom3.latch_to_present(), HistogramEq(buildExpectedHistogram({2}, {1})));
+    EXPECT_THAT(atom3.desired_to_present(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_THAT(atom3.post_to_acquire(), HistogramEq(buildExpectedHistogram({1}, {1})));
+    EXPECT_EQ(atom3.late_acquire_frames(), LATE_ACQUIRE_FRAMES);
+    EXPECT_EQ(atom3.bad_desired_present_frames(), BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_EQ(atom3.uid(), UID_0);
+    EXPECT_EQ(atom3.display_refresh_rate_bucket(), REFRESH_RATE_BUCKET_0);
+    EXPECT_EQ(atom3.render_rate_bucket(), RENDER_RATE_BUCKET_0);
+    EXPECT_EQ(atom3.game_mode(), SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD);
 }
 
 TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
@@ -1393,8 +1461,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1418,8 +1488,10 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1437,8 +1509,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1457,8 +1531,10 @@
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
 
+    std::vector<uint8_t> pulledBytes;
+    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
     std::string pulledData;
-    EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+    pulledData.assign(pulledBytes.begin(), pulledBytes.end());
 
     SurfaceflingerStatsLayerInfoWrapper atomList;
     ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index ded7531..afb8efb 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
@@ -22,22 +21,27 @@
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/LayerState.h>
 #include <gui/SurfaceComposerClient.h>
+#include <gui/fake/BufferData.h>
 #include <log/log.h>
 #include <ui/MockFence.h>
 #include <utils/String8.h>
+#include <vector>
+#include <binder/Binder.h>
 
+#include "FrontEnd/TransactionHandler.h"
 #include "TestableSurfaceFlinger.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
+#include "TransactionState.h"
 
 namespace android {
 
 using testing::_;
 using testing::Return;
 
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using frontend::TransactionHandler;
 
+constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5);
 class TransactionApplicationTest : public testing::Test {
 public:
     TransactionApplicationTest() {
@@ -45,7 +49,9 @@
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-        setupScheduler();
+        mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+        mFlinger.setupMockScheduler();
+        mFlinger.flinger()->addTransactionReadyFilters();
     }
 
     ~TransactionApplicationTest() {
@@ -54,35 +60,8 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    void setupScheduler() {
-        auto eventThread = std::make_unique<mock::EventThread>();
-        auto sfEventThread = std::make_unique<mock::EventThread>();
-
-        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*mVSyncTracker, currentPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-
-        mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-        mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
-                                std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
-                                std::move(eventThread), std::move(sfEventThread));
-    }
-
     TestableSurfaceFlinger mFlinger;
 
-    mock::VsyncController* mVsyncController = new mock::VsyncController();
-    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
-
     struct TransactionInfo {
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
@@ -92,8 +71,9 @@
         int64_t desiredPresentTime = 0;
         bool isAutoTimestamp = true;
         FrameTimelineInfo frameTimelineInfo;
-        client_cache_t uncacheBuffer;
+        std::vector<client_cache_t> uncacheBuffers;
         uint64_t id = static_cast<uint64_t>(-1);
+        std::vector<uint64_t> mergedTransactionIds;
         static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
     };
 
@@ -107,22 +87,20 @@
         EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
     }
 
-    void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
-                     int64_t desiredPresentTime, bool isAutoTimestamp,
-                     const FrameTimelineInfo& frameTimelineInfo) {
+    void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
+                     bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
         mTransactionNumber++;
-        transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
-        transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
+        transaction.flags |= flags;
         transaction.desiredPresentTime = desiredPresentTime;
         transaction.isAutoTimestamp = isAutoTimestamp;
         transaction.frameTimelineInfo = frameTimelineInfo;
     }
 
-    void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+    void NotPlacedOnTransactionQueue(uint32_t flags) {
+        ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
         TransactionInfo transaction;
-        setupSingle(transaction, flags, syncInputWindows,
+        setupSingle(transaction, flags,
                     /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
                     FrameTimelineInfo{});
         nsecs_t applicationTime = systemTime();
@@ -130,87 +108,71 @@
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transaction.id);
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id, transaction.mergedTransactionIds);
 
-        // If transaction is synchronous or syncs input windows, SF
-        // applyTransactionState should time out (5s) wating for SF to commit
-        // the transaction or to receive a signal that syncInputWindows has
-        // completed.  If this is animation, it should not time out waiting.
+        // If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
+        // SF to commit the transaction. If this is animation, it should not time out waiting.
         nsecs_t returnedTime = systemTime();
-        if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) {
-            EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(returnedTime, applicationTime + TRANSACTION_TIMEOUT);
         // Each transaction should have been placed on the transaction queue
-        auto transactionQueue = mFlinger.getTransactionQueue();
-        EXPECT_EQ(1u, transactionQueue.size());
+        auto& transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_FALSE(transactionQueue.isEmpty());
     }
 
-    void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+    void PlaceOnTransactionQueue(uint32_t flags) {
+        ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
         // first check will see desired present time has not passed,
         // but afterwards it will look like the desired present time has passed
         nsecs_t time = systemTime();
         TransactionInfo transaction;
-        setupSingle(transaction, flags, syncInputWindows,
-                    /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
+        setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false,
+                    FrameTimelineInfo{});
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
                                      transaction.displays, transaction.flags,
                                      transaction.applyToken, transaction.inputWindowCommands,
                                      transaction.desiredPresentTime, transaction.isAutoTimestamp,
-                                     transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transaction.id);
+                                     transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+                                     transaction.id, transaction.mergedTransactionIds);
 
         nsecs_t returnedTime = systemTime();
-        if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) {
-            EXPECT_GE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(returnedTime,
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
         // This transaction should have been placed on the transaction queue
-        auto transactionQueue = mFlinger.getTransactionQueue();
-        EXPECT_EQ(1u, transactionQueue.size());
+        auto& transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_FALSE(transactionQueue.isEmpty());
     }
 
-    void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
-        ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+    void BlockedByPriorTransaction(uint32_t flags) {
+        ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         nsecs_t time = systemTime();
-        if (!syncInputWindows) {
-            EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
-        } else {
-            EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-        }
+        EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
+
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
-        setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                    /*desiredPresentTime*/ time + s2ns(1), false, FrameTimelineInfo{});
+        setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ time + s2ns(1), false,
+                    FrameTimelineInfo{});
 
         // transaction that would not have gone on the pending thread if not
         // blocked
         TransactionInfo transactionB;
-        setupSingle(transactionB, flags, syncInputWindows,
-                    /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
-                    FrameTimelineInfo{});
+        setupSingle(transactionB, flags, /*desiredPresentTime*/ systemTime(),
+                    /*isAutoTimestamp*/ true, FrameTimelineInfo{});
 
         nsecs_t applicationSentTime = systemTime();
         mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                      transactionA.displays, transactionA.flags,
                                      transactionA.applyToken, transactionA.inputWindowCommands,
                                      transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
-                                     transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionA.id);
+                                     transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+                                     transactionA.id, transactionA.mergedTransactionIds);
 
         // This thread should not have been blocked by the above transaction
         // (5s is the timeout period that applyTransactionState waits for SF to
         // commit the transaction)
-        EXPECT_LE(systemTime(), applicationSentTime + mFlinger.getAnimationTransactionTimeout());
+        EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
         // transaction that would goes to pending transaciton queue.
         mFlinger.flushTransactionQueues();
 
@@ -219,21 +181,14 @@
                                      transactionB.displays, transactionB.flags,
                                      transactionB.applyToken, transactionB.inputWindowCommands,
                                      transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
-                                     transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
-                                     transactionB.id);
+                                     transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+                                     transactionB.id, transactionB.mergedTransactionIds);
 
         // this thread should have been blocked by the above transaction
         // if this is an animation, this thread should be blocked for 5s
         // in setTransactionState waiting for transactionA to flush.  Otherwise,
         // the transaction should be placed on the pending queue
-        if (flags & (ISurfaceComposer::eSynchronous) ||
-            syncInputWindows) {
-            EXPECT_GE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        } else {
-            EXPECT_LE(systemTime(),
-                      applicationSentTime + mFlinger.getAnimationTransactionTimeout());
-        }
+        EXPECT_LE(systemTime(), applicationSentTime + TRANSACTION_TIMEOUT);
 
         // transaction that would goes to pending transaciton queue.
         mFlinger.flushTransactionQueues();
@@ -243,29 +198,53 @@
         EXPECT_EQ(0u, transactionQueue.size());
     }
 
+    void modulateVsync() {
+        static_cast<void>(
+                mFlinger.mutableScheduler().vsyncModulator().onRefreshRateChangeInitiated());
+    }
+
     bool mHasListenerCallbacks = false;
     std::vector<ListenerCallbacks> mCallbacks;
     int mTransactionNumber = 0;
 };
 
-TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
-    ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+TEST_F(TransactionApplicationTest, AddToPendingQueue) {
+    ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
     EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
 
     TransactionInfo transactionA; // transaction to go on pending queue
-    setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
-                /*desiredPresentTime*/ s2ns(1), false, FrameTimelineInfo{});
+    setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
+                FrameTimelineInfo{});
     mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
                                  transactionA.displays, transactionA.flags, transactionA.applyToken,
                                  transactionA.inputWindowCommands, transactionA.desiredPresentTime,
-                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
-                                 mHasListenerCallbacks, mCallbacks, transactionA.id);
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
+                                 mHasListenerCallbacks, mCallbacks, transactionA.id,
+                                 transactionA.mergedTransactionIds);
 
     auto& transactionQueue = mFlinger.getTransactionQueue();
-    ASSERT_EQ(1u, transactionQueue.size());
+    ASSERT_FALSE(transactionQueue.isEmpty());
 
-    auto& transactionState = transactionQueue.front();
+    auto transactionState = transactionQueue.pop().value();
     checkEqual(transactionA, transactionState);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
+    ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+
+    TransactionInfo transactionA; // transaction to go on pending queue
+    setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
+                FrameTimelineInfo{});
+    mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+                                 transactionA.displays, transactionA.flags, transactionA.applyToken,
+                                 transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+                                 transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
+                                 mHasListenerCallbacks, mCallbacks, transactionA.id,
+                                 transactionA.mergedTransactionIds);
+
+    auto& transactionQueue = mFlinger.getTransactionQueue();
+    ASSERT_FALSE(transactionQueue.isEmpty());
 
     // because flushing uses the cached expected present time, we send an empty
     // transaction here (sending a null applyToken to fake it as from a
@@ -275,57 +254,67 @@
     mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
                                  empty.applyToken, empty.inputWindowCommands,
                                  empty.desiredPresentTime, empty.isAutoTimestamp,
-                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
+                                 empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
+                                 empty.mergedTransactionIds);
 
     // flush transaction queue should flush as desiredPresentTime has
     // passed
     mFlinger.flushTransactionQueues();
 
-    EXPECT_EQ(0u, transactionQueue.size());
-}
-
-TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
-    NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+    EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
 }
 
 TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
-    NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
-}
-
-TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
-    PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+    NotPlacedOnTransactionQueue(/*flags*/ 0);
 }
 
 TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
-    PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
-}
-
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
-    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
-}
-
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) {
-    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
-}
-
-TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) {
-    BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
+    PlaceOnTransactionQueue(/*flags*/ 0);
 }
 
 TEST_F(TransactionApplicationTest, FromHandle) {
     sp<IBinder> badHandle;
     auto ret = mFlinger.fromHandle(badHandle);
-    EXPECT_EQ(nullptr, ret.promote().get());
+    EXPECT_EQ(nullptr, ret.get());
 }
 
+class FakeExternalTexture : public renderengine::ExternalTexture {
+    const sp<GraphicBuffer> mEmptyBuffer = nullptr;
+    uint32_t mWidth;
+    uint32_t mHeight;
+    uint64_t mId;
+    PixelFormat mPixelFormat;
+    uint64_t mUsage;
+
+public:
+    FakeExternalTexture(BufferData& bufferData)
+          : mWidth(bufferData.getWidth()),
+            mHeight(bufferData.getHeight()),
+            mId(bufferData.getId()),
+            mPixelFormat(bufferData.getPixelFormat()),
+            mUsage(bufferData.getUsage()) {}
+    const sp<GraphicBuffer>& getBuffer() const { return mEmptyBuffer; }
+    bool hasSameBuffer(const renderengine::ExternalTexture& other) const override {
+        return getId() == other.getId();
+    }
+    uint32_t getWidth() const override { return mWidth; }
+    uint32_t getHeight() const override { return mHeight; }
+    uint64_t getId() const override { return mId; }
+    PixelFormat getPixelFormat() const override { return mPixelFormat; }
+    uint64_t getUsage() const override { return mUsage; }
+    void remapBuffer() override {}
+    ~FakeExternalTexture() = default;
+};
+
 class LatchUnsignaledTest : public TransactionApplicationTest {
 public:
     void TearDown() override {
         // Clear all transaction queues to release all transactions we sent
         // in the tests. Otherwise, gmock complains about memory leaks.
-        mFlinger.getTransactionQueue().clear();
+        while (!mFlinger.getTransactionQueue().isEmpty()) {
+            mFlinger.getTransactionQueue().pop();
+        }
         mFlinger.getPendingTransactionQueue().clear();
-        mFlinger.getTransactionCommittedSignals().clear();
         mFlinger.commitTransactionsLocked(eTransactionMask);
         mFlinger.mutableCurrentState().layersSortedByZ.clear();
         mFlinger.mutableDrawingState().layersSortedByZ.clear();
@@ -337,35 +326,41 @@
         return fence;
     }
 
-    ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what) {
+    ComposerState createComposerState(int layerId, sp<Fence> fence, uint64_t what,
+                                      std::optional<sp<IBinder>> layerHandle = std::nullopt) {
         ComposerState state;
-        state.state.bufferData = std::make_shared<BufferData>();
+        state.state.bufferData =
+                std::make_shared<fake::BufferData>(/* bufferId */ 123L, /* width */ 1,
+                                                   /* height */ 2, /* pixelFormat */ 0,
+                                                   /* outUsage */ 0);
         state.state.bufferData->acquireFence = std::move(fence);
         state.state.layerId = layerId;
-        state.state.surface =
-                sp<BufferStateLayer>::make(
-                        LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
-                        ->getHandle();
+        state.state.surface = layerHandle.value_or(
+                sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}))
+                        ->getHandle());
         state.state.bufferData->flags = BufferData::BufferDataChange::fenceChanged;
 
         state.state.what = what;
         if (what & layer_state_t::eCropChanged) {
             state.state.crop = Rect(1, 2, 3, 4);
         }
+        if (what & layer_state_t::eFlagsChanged) {
+            state.state.flags = layer_state_t::eEnableBackpressure;
+            state.state.mask = layer_state_t::eEnableBackpressure;
+        }
+
         return state;
     }
 
     TransactionInfo createTransactionInfo(const sp<IBinder>& applyToken,
                                           const std::vector<ComposerState>& states) {
         TransactionInfo transaction;
-        const uint32_t kFlags = ISurfaceComposer::eSynchronous;
-        const bool kSyncInputWindows = false;
+        const uint32_t kFlags = 0;
         const nsecs_t kDesiredPresentTime = systemTime();
         const bool kIsAutoTimestamp = true;
         const auto kFrameTimelineInfo = FrameTimelineInfo{};
 
-        setupSingle(transaction, kFlags, kSyncInputWindows, kDesiredPresentTime, kIsAutoTimestamp,
-                    kFrameTimelineInfo);
+        setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo);
         transaction.applyToken = applyToken;
         for (const auto& state : states) {
             transaction.states.push_back(state);
@@ -375,23 +370,35 @@
     }
 
     void setTransactionStates(const std::vector<TransactionInfo>& transactions,
-                              size_t expectedTransactionsApplied,
                               size_t expectedTransactionsPending) {
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+        EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
         EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
 
-        for (const auto& transaction : transactions) {
-            mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
-                                         transaction.displays, transaction.flags,
-                                         transaction.applyToken, transaction.inputWindowCommands,
-                                         transaction.desiredPresentTime,
-                                         transaction.isAutoTimestamp, transaction.uncacheBuffer,
-                                         mHasListenerCallbacks, mCallbacks, transaction.id);
+        for (auto transaction : transactions) {
+            std::vector<ResolvedComposerState> resolvedStates;
+            resolvedStates.reserve(transaction.states.size());
+            for (auto& state : transaction.states) {
+                ResolvedComposerState resolvedState;
+                resolvedState.state = std::move(state.state);
+                resolvedState.externalTexture =
+                        std::make_shared<FakeExternalTexture>(*resolvedState.state.bufferData);
+                resolvedStates.emplace_back(resolvedState);
+            }
+
+            TransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
+                                              transaction.displays, transaction.flags,
+                                              transaction.applyToken,
+                                              transaction.inputWindowCommands,
+                                              transaction.desiredPresentTime,
+                                              transaction.isAutoTimestamp, {}, systemTime(),
+                                              mHasListenerCallbacks, mCallbacks, getpid(),
+                                              static_cast<int>(getuid()), transaction.id,
+                                              transaction.mergedTransactionIds);
+            mFlinger.setTransactionStateInternal(transactionState);
         }
         mFlinger.flushTransactionQueues();
-        EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
-        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionQueue().size());
-        EXPECT_EQ(expectedTransactionsApplied, mFlinger.getTransactionCommittedSignals().size());
+        EXPECT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+        EXPECT_EQ(expectedTransactionsPending, mFlinger.getPendingTransactionCount());
     }
 };
 
@@ -407,22 +414,19 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSingleUnSignaledFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -432,33 +436,13 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eCropChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -470,15 +454,31 @@
                                                                       layer_state_t::
                                                                               eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId = 1;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eCropChanged |
+                                                                      layer_state_t::
+                                                                              eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueueSameApplyTokenMultiState) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto mixedTransaction =
@@ -491,8 +491,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsInTheQueue_MultipleStateTransaction) {
@@ -500,7 +499,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto mixedTransaction =
@@ -513,8 +511,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesSignaledFromTheQueue) {
@@ -522,7 +519,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -539,8 +535,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest,
@@ -551,7 +546,6 @@
     const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -578,7 +572,76 @@
                                   });
 
     setTransactionStates({unsignaledTransaction, signaledTransaction, signaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto signaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Signaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
+                         kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueue) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+    const auto kLayerId1 = 1;
+    const auto kLayerId2 = 2;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    const auto unsignaledTransaction2 =
+            createTransactionInfo(kApplyToken2,
+                                  {
+                                          createComposerState(kLayerId2,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
+                         kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) {
+    const sp<IBinder> kApplyToken =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const auto kLayerId = 1;
+    const auto kExpectedTransactionsPending = 1u;
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken,
+                                  {
+                                          createComposerState(kLayerId,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged),
+                                  });
+
+    modulateVsync();
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAutoSingleLayerTest, UnsignaledNotAppliedWhenThereAreSignaled_SignaledFirst) {
@@ -588,7 +651,6 @@
     const sp<IBinder> kApplyToken3 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto signaledTransaction =
@@ -614,81 +676,6 @@
                                   });
 
     setTransactionStates({signaledTransaction, signaledTransaction2, unsignaledTransaction},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueueSameApplyToken) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto signaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Signaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsTransactionInTheQueue) {
-    const sp<IBinder> kApplyToken1 =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
-    const auto kLayerId1 = 1;
-    const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken1,
-                                  {
-                                          createComposerState(kLayerId1,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    const auto unsignaledTransaction2 =
-            createTransactionInfo(kApplyToken2,
-                                  {
-                                          createComposerState(kLayerId2,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
-}
-
-TEST_F(LatchUnsignaledAutoSingleLayerTest, DontLatchUnsignaledWhenEarlyOffset) {
-    const sp<IBinder> kApplyToken =
-            IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
-    const auto kExpectedTransactionsPending = 1u;
-
-    const auto unsignaledTransaction =
-            createTransactionInfo(kApplyToken,
-                                  {
-                                          createComposerState(kLayerId,
-                                                              fence(Fence::Status::Unsignaled),
-                                                              layer_state_t::eBufferChanged),
-                                  });
-
-    // Get VsyncModulator out of the default config
-    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
-
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
                          kExpectedTransactionsPending);
 }
 
@@ -704,22 +691,19 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -729,15 +713,13 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueSameLayerId) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -750,8 +732,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepsInTheQueueDifferentLayerId) {
@@ -759,7 +740,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -772,8 +752,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -781,7 +760,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -798,8 +776,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledDisabledTest, Flush_KeepInTheQueueDifferentApplyToken) {
@@ -808,7 +785,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto unsignaledTransaction =
@@ -825,7 +801,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -834,7 +810,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 1u;
 
     const auto signaledTransaction =
@@ -851,7 +826,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({signaledTransaction, unsignaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -860,8 +835,7 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 0u;
-    const auto kExpectedTransactionsPending = 1u;
+    const auto kExpectedTransactionsPending = 2u;
 
     const auto unsignaledTransaction =
             createTransactionInfo(kApplyToken,
@@ -878,7 +852,7 @@
                                                               layer_state_t::eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
 }
 
 class LatchUnsignaledAlwaysTest : public LatchUnsignaledTest {
@@ -893,37 +867,32 @@
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({signaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueue) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
             createTransactionInfo(kApplyToken,
                                   {createComposerState(kLayerId, fence(Fence::Status::Unsignaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueSameLayerId) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto mixedTransaction =
@@ -932,8 +901,7 @@
                                                        layer_state_t::eBufferChanged),
                                    createComposerState(kLayerId, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentLayerId) {
@@ -941,7 +909,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto mixedTransaction =
@@ -950,8 +917,7 @@
                                                        layer_state_t::eBufferChanged),
                                    createComposerState(kLayerId2, fence(Fence::Status::Signaled),
                                                        layer_state_t::eBufferChanged)});
-    setTransactionStates({mixedTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({mixedTransaction}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesSignaledFromTheQueue_MultipleLayers) {
@@ -959,7 +925,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -976,8 +941,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+    setTransactionStates({signaledTransaction, signaledTransaction2}, kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, Flush_RemovesFromTheQueueDifferentApplyToken) {
@@ -986,7 +950,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto signaledTransaction =
@@ -1003,7 +966,7 @@
                                                               fence(Fence::Status::Unsignaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({signaledTransaction, unsignaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({signaledTransaction, unsignaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -1012,7 +975,6 @@
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1029,7 +991,7 @@
                                                               fence(Fence::Status::Signaled),
                                                               layer_state_t::eBufferChanged),
                                   });
-    setTransactionStates({unsignaledTransaction, signaledTransaction}, kExpectedTransactionsApplied,
+    setTransactionStates({unsignaledTransaction, signaledTransaction},
                          kExpectedTransactionsPending);
 }
 
@@ -1039,7 +1001,6 @@
     const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
     const auto kLayerId1 = 1;
     const auto kLayerId2 = 2;
-    const auto kExpectedTransactionsApplied = 2u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1057,14 +1018,50 @@
                                                               layer_state_t::eBufferChanged),
                                   });
     setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
-                         kExpectedTransactionsApplied, kExpectedTransactionsPending);
+                         kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAlwaysTest, RespectsBackPressureFlag) {
+    const sp<IBinder> kApplyToken1 =
+            IInterface::asBinder(TransactionCompletedListener::getIInstance());
+    const sp<IBinder> kApplyToken2 = sp<BBinder>::make();
+    const auto kLayerId1 = 1;
+    const auto kExpectedTransactionsPending = 1u;
+    auto layer =
+            sp<Layer>::make(LayerCreationArgs(mFlinger.flinger(), nullptr, "TestLayer", 0, {}));
+    auto layerHandle = layer->getHandle();
+    const auto setBackPressureFlagTransaction =
+            createTransactionInfo(kApplyToken1,
+                                  {createComposerState(kLayerId1, fence(Fence::Status::Unsignaled),
+                                                       layer_state_t::eBufferChanged |
+                                                               layer_state_t::eFlagsChanged,
+                                                       {layerHandle})});
+    setTransactionStates({setBackPressureFlagTransaction}, 0u);
+
+    const auto unsignaledTransaction =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged,
+                                                              {layerHandle}),
+                                  });
+    const auto unsignaledTransaction2 =
+            createTransactionInfo(kApplyToken1,
+                                  {
+                                          createComposerState(kLayerId1,
+                                                              fence(Fence::Status::Unsignaled),
+                                                              layer_state_t::eBufferChanged,
+                                                              {layerHandle}),
+                                  });
+    setTransactionStates({unsignaledTransaction, unsignaledTransaction2},
+                         kExpectedTransactionsPending);
 }
 
 TEST_F(LatchUnsignaledAlwaysTest, LatchUnsignaledWhenEarlyOffset) {
     const sp<IBinder> kApplyToken =
             IInterface::asBinder(TransactionCompletedListener::getIInstance());
     const auto kLayerId = 1;
-    const auto kExpectedTransactionsApplied = 1u;
     const auto kExpectedTransactionsPending = 0u;
 
     const auto unsignaledTransaction =
@@ -1075,11 +1072,153 @@
                                                               layer_state_t::eBufferChanged),
                                   });
 
-    // Get VsyncModulator out of the default config
-    static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
+    modulateVsync();
+    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
 
-    setTransactionStates({unsignaledTransaction}, kExpectedTransactionsApplied,
-                         kExpectedTransactionsPending);
+TEST(TransactionHandlerTest, QueueTransaction) {
+    TransactionHandler handler;
+    TransactionState transaction;
+    transaction.applyToken = sp<BBinder>::make();
+    transaction.id = 42;
+    handler.queueTransaction(std::move(transaction));
+    std::vector<TransactionState> transactionsReadyToBeApplied = handler.flushTransactions();
+
+    EXPECT_EQ(transactionsReadyToBeApplied.size(), 1u);
+    EXPECT_EQ(transactionsReadyToBeApplied.front().id, 42u);
+}
+
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfDirectMerges) {
+    SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+    uint64_t transaction2Id = transaction2.getId();
+    uint64_t transaction3Id = transaction3.getId();
+    EXPECT_NE(transaction2Id, transaction3Id);
+
+    transaction1.merge(std::move(transaction2));
+    transaction1.merge(std::move(transaction3));
+
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionsKeepTrackOfIndirectMerges) {
+    SurfaceComposerClient::Transaction transaction1, transaction2, transaction3, transaction4;
+
+    uint64_t transaction2Id = transaction2.getId();
+    uint64_t transaction3Id = transaction3.getId();
+    uint64_t transaction4Id = transaction4.getId();
+    EXPECT_NE(transaction2Id, transaction3Id);
+    EXPECT_NE(transaction2Id, transaction4Id);
+    EXPECT_NE(transaction3Id, transaction4Id);
+
+    transaction4.merge(std::move(transaction2));
+    transaction4.merge(std::move(transaction3));
+
+    EXPECT_EQ(transaction4.getMergedTransactionIds().size(), 2u);
+    EXPECT_EQ(transaction4.getMergedTransactionIds()[0], transaction3Id);
+    EXPECT_EQ(transaction4.getMergedTransactionIds()[1], transaction2Id);
+
+    transaction1.merge(std::move(transaction4));
+
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 3u);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction4Id);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[1], transaction3Id);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[2], transaction2Id);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCleared) {
+    SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+
+    transaction1.merge(std::move(transaction2));
+    transaction1.merge(std::move(transaction3));
+
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 2u);
+
+    transaction1.clear();
+
+    EXPECT_EQ(transaction1.getMergedTransactionIds().empty(), true);
+}
+
+TEST(TransactionHandlerTest, TransactionMergesAreCapped) {
+    SurfaceComposerClient::Transaction transaction;
+    std::vector<uint64_t> mergedTransactionIds;
+
+    for (uint i = 0; i < 20u; i++) {
+        SurfaceComposerClient::Transaction transactionToMerge;
+        mergedTransactionIds.push_back(transactionToMerge.getId());
+        transaction.merge(std::move(transactionToMerge));
+    }
+
+    // Keeps latest 10 merges in order of merge recency
+    EXPECT_EQ(transaction.getMergedTransactionIds().size(), 10u);
+    for (uint i = 0; i < 10u; i++) {
+        EXPECT_EQ(transaction.getMergedTransactionIds()[i],
+                  mergedTransactionIds[mergedTransactionIds.size() - 1 - i]);
+    }
+}
+
+TEST(TransactionHandlerTest, KeepsMergesFromMoreRecentMerge) {
+    SurfaceComposerClient::Transaction transaction1, transaction2, transaction3;
+    std::vector<uint64_t> mergedTransactionIds1, mergedTransactionIds2, mergedTransactionIds3;
+    uint64_t transaction2Id = transaction2.getId();
+    uint64_t transaction3Id = transaction3.getId();
+
+    for (uint i = 0; i < 20u; i++) {
+        SurfaceComposerClient::Transaction transactionToMerge;
+        mergedTransactionIds1.push_back(transactionToMerge.getId());
+        transaction1.merge(std::move(transactionToMerge));
+    }
+
+    for (uint i = 0; i < 5u; i++) {
+        SurfaceComposerClient::Transaction transactionToMerge;
+        mergedTransactionIds2.push_back(transactionToMerge.getId());
+        transaction2.merge(std::move(transactionToMerge));
+    }
+
+    transaction1.merge(std::move(transaction2));
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction2Id);
+    for (uint i = 0; i < 5u; i++) {
+        EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1u],
+                  mergedTransactionIds2[mergedTransactionIds2.size() - 1 - i]);
+    }
+    for (uint i = 0; i < 4u; i++) {
+        EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 6u],
+                  mergedTransactionIds1[mergedTransactionIds1.size() - 1 - i]);
+    }
+
+    for (uint i = 0; i < 20u; i++) {
+        SurfaceComposerClient::Transaction transactionToMerge;
+        mergedTransactionIds3.push_back(transactionToMerge.getId());
+        transaction3.merge(std::move(transactionToMerge));
+    }
+
+    transaction1.merge(std::move(transaction3));
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+    EXPECT_EQ(transaction1.getMergedTransactionIds()[0], transaction3Id);
+    for (uint i = 0; i < 9u; i++) {
+        EXPECT_EQ(transaction1.getMergedTransactionIds()[i + 1],
+                  mergedTransactionIds3[mergedTransactionIds3.size() - 1 - i]);
+    }
+}
+
+TEST(TransactionHandlerTest, CanAddTransactionWithFullMergedIds) {
+    SurfaceComposerClient::Transaction transaction1, transaction2;
+    for (uint i = 0; i < 20u; i++) {
+        SurfaceComposerClient::Transaction transactionToMerge;
+        transaction1.merge(std::move(transactionToMerge));
+    }
+
+    EXPECT_EQ(transaction1.getMergedTransactionIds().size(), 10u);
+
+    auto transaction1Id = transaction1.getId();
+    transaction2.merge(std::move(transaction1));
+    EXPECT_EQ(transaction2.getMergedTransactionIds().size(), 10u);
+    auto mergedTransactionIds = transaction2.getMergedTransactionIds();
+    EXPECT_TRUE(std::count(mergedTransactionIds.begin(), mergedTransactionIds.end(),
+                           transaction1Id) > 0);
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 5364630..764d19b 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -28,15 +28,13 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
 
 namespace android {
 
 using testing::_;
 using testing::Mock;
 using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
 using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 class TransactionFrameTracerTest : public testing::Test {
@@ -45,7 +43,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-        setupScheduler();
+        mFlinger.setupMockScheduler();
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     }
@@ -56,11 +54,11 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
         LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
                                LayerMetadata());
-        return new BufferStateLayer(args);
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -68,40 +66,15 @@
         layer->commitTransaction(c);
     }
 
-    void setupScheduler() {
-        auto eventThread = std::make_unique<mock::EventThread>();
-        auto sfEventThread = std::make_unique<mock::EventThread>();
-
-        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        auto vsyncController = std::make_unique<mock::VsyncController>();
-        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*vsyncTracker, currentPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                std::move(eventThread), std::move(sfEventThread));
-    }
-
     TestableSurfaceFlinger mFlinger;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
 
     FenceToFenceTimeMap fenceFactory;
 
     void BLASTTransactionSendsFrameTracerEvents() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
-        sp<Fence> fence(new Fence());
+        sp<Fence> fence(sp<Fence>::make());
         int32_t layerId = layer->getSequence();
         uint64_t bufferId = 42;
         uint64_t frameNumber = 5;
@@ -127,7 +100,6 @@
                          dequeueTime, FrameTimelineInfo{});
 
         commitTransaction(layer.get());
-        bool computeVisisbleRegions;
         nsecs_t latchTime = 25;
         EXPECT_CALL(*mFlinger.getFrameTracer(),
                     traceFence(layerId, bufferId, frameNumber, _,
@@ -135,7 +107,7 @@
         EXPECT_CALL(*mFlinger.getFrameTracer(),
                     traceTimestamp(layerId, bufferId, frameNumber, latchTime,
                                    FrameTracer::FrameEvent::LATCH, /*duration*/ 0));
-        layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0);
+        layer->updateTexImage(latchTime);
 
         auto glDoneFence = fenceFactory.createFenceTimeForTest(fence);
         auto presentFence = fenceFactory.createFenceTimeForTest(fence);
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index f5e3b77..dd72174 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -19,6 +19,8 @@
 #include <limits> // std::numeric_limits
 
 #include <gui/SurfaceComposerClient.h>
+#include <ui/Rotation.h>
+#include "LayerProtoHelper.h"
 
 #include "Tracing/TransactionProtoParser.h"
 
@@ -27,8 +29,7 @@
 namespace android {
 
 TEST(TransactionProtoParserTest, parse) {
-    const sp<IBinder> layerHandle = new BBinder();
-    const sp<IBinder> displayHandle = new BBinder();
+    const sp<IBinder> displayHandle = sp<BBinder>::make();
     TransactionState t1;
     t1.originPid = 1;
     t1.originUid = 2;
@@ -37,7 +38,6 @@
     t1.postTime = 5;
 
     layer_state_t layer;
-    layer.layerId = 6;
     layer.what = std::numeric_limits<uint64_t>::max();
     layer.what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
     layer.x = 7;
@@ -46,14 +46,13 @@
     size_t layerCount = 2;
     t1.states.reserve(layerCount);
     for (uint32_t i = 0; i < layerCount; i++) {
-        ComposerState s;
+        ResolvedComposerState s;
         if (i == 1) {
-            layer.parentSurfaceControlForChild =
-                    new SurfaceControl(SurfaceComposerClient::getDefault(), layerHandle, nullptr,
-                                       42);
+            s.parentId = 42;
         }
+        s.layerId = 6 + i;
         s.state = layer;
-        t1.states.add(s);
+        t1.states.emplace_back(s);
     }
 
     size_t displayCount = 2;
@@ -72,18 +71,10 @@
 
     class TestMapper : public TransactionProtoParser::FlingerDataMapper {
     public:
-        sp<IBinder> layerHandle;
         sp<IBinder> displayHandle;
 
-        TestMapper(sp<IBinder> layerHandle, sp<IBinder> displayHandle)
-              : layerHandle(layerHandle), displayHandle(displayHandle) {}
+        TestMapper(sp<IBinder> displayHandle) : displayHandle(displayHandle) {}
 
-        sp<IBinder> getLayerHandle(int32_t id) const override {
-            return (id == 42) ? layerHandle : nullptr;
-        }
-        int64_t getLayerId(const sp<IBinder>& handle) const override {
-            return (handle == layerHandle) ? 42 : -1;
-        }
         sp<IBinder> getDisplayHandle(int32_t id) const {
             return (id == 43) ? displayHandle : nullptr;
         }
@@ -92,7 +83,7 @@
         }
     };
 
-    TransactionProtoParser parser(std::make_unique<TestMapper>(layerHandle, displayHandle));
+    TransactionProtoParser parser(std::make_unique<TestMapper>(displayHandle));
 
     proto::TransactionState proto = parser.toProto(t1);
     TransactionState t2 = parser.fromProto(proto);
@@ -105,12 +96,56 @@
     ASSERT_EQ(t1.states.size(), t2.states.size());
     ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
     ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
-    ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
-              t2.states[1].state.parentSurfaceControlForChild->getHandle());
+    ASSERT_EQ(t1.states[1].layerId, t2.states[1].layerId);
+    ASSERT_EQ(t1.states[1].parentId, t2.states[1].parentId);
 
     ASSERT_EQ(t1.displays.size(), t2.displays.size());
     ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
     ASSERT_EQ(t1.displays[0].token, t2.displays[0].token);
 }
 
+TEST(TransactionProtoParserTest, parseDisplayInfo) {
+    frontend::DisplayInfo d1;
+    d1.info.displayId = 42;
+    d1.info.logicalWidth = 43;
+    d1.info.logicalHeight = 44;
+    d1.info.transform.set(1, 2, 3, 4);
+    d1.transform = d1.info.transform.inverse();
+    d1.receivesInput = true;
+    d1.isSecure = false;
+    d1.isPrimary = true;
+    d1.isVirtual = false;
+    d1.rotationFlags = ui::Transform::ROT_180;
+    d1.transformHint = ui::Transform::ROT_90;
+
+    const uint32_t layerStack = 2;
+    google::protobuf::RepeatedPtrField<proto::DisplayInfo> displayProtos;
+    auto displayInfoProto = displayProtos.Add();
+    *displayInfoProto = TransactionProtoParser::toProto(d1, layerStack);
+    display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
+    TransactionProtoParser::fromProto(displayProtos, displayInfos);
+
+    ASSERT_TRUE(displayInfos.contains(ui::LayerStack::fromValue(layerStack)));
+    frontend::DisplayInfo d2 = displayInfos.get(ui::LayerStack::fromValue(layerStack))->get();
+    EXPECT_EQ(d1.info.displayId, d2.info.displayId);
+    EXPECT_EQ(d1.info.logicalWidth, d2.info.logicalWidth);
+    EXPECT_EQ(d1.info.logicalHeight, d2.info.logicalHeight);
+
+    EXPECT_EQ(d1.info.transform.dsdx(), d2.info.transform.dsdx());
+    EXPECT_EQ(d1.info.transform.dsdy(), d2.info.transform.dsdy());
+    EXPECT_EQ(d1.info.transform.dtdx(), d2.info.transform.dtdx());
+    EXPECT_EQ(d1.info.transform.dtdy(), d2.info.transform.dtdy());
+
+    EXPECT_EQ(d1.transform.dsdx(), d2.transform.dsdx());
+    EXPECT_EQ(d1.transform.dsdy(), d2.transform.dsdy());
+    EXPECT_EQ(d1.transform.dtdx(), d2.transform.dtdx());
+    EXPECT_EQ(d1.transform.dtdy(), d2.transform.dtdy());
+
+    EXPECT_EQ(d1.receivesInput, d2.receivesInput);
+    EXPECT_EQ(d1.isSecure, d2.isSecure);
+    EXPECT_EQ(d1.isVirtual, d2.isVirtual);
+    EXPECT_EQ(d1.rotationFlags, d2.rotationFlags);
+    EXPECT_EQ(d1.transformHint, d2.transformHint);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 5bb4c92..e2c6491 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -28,15 +28,13 @@
 
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockVsyncController.h"
 
 namespace android {
 
 using testing::_;
 using testing::Mock;
 using testing::Return;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
 using PresentState = frametimeline::SurfaceFrame::PresentState;
 
 class TransactionSurfaceFrameTest : public testing::Test {
@@ -45,7 +43,7 @@
         const ::testing::TestInfo* const test_info =
                 ::testing::UnitTest::GetInstance()->current_test_info();
         ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-        setupScheduler();
+        mFlinger.setupMockScheduler();
         mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     }
@@ -56,11 +54,10 @@
         ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
     }
 
-    sp<BufferStateLayer> createBufferStateLayer() {
+    sp<Layer> createLayer() {
         sp<Client> client;
-        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
-                               LayerMetadata());
-        return new BufferStateLayer(args);
+        LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
+        return sp<Layer>::make(args);
     }
 
     void commitTransaction(Layer* layer) {
@@ -68,40 +65,17 @@
         layer->commitTransaction(c);
     }
 
-    void setupScheduler() {
-        auto eventThread = std::make_unique<mock::EventThread>();
-        auto sfEventThread = std::make_unique<mock::EventThread>();
-
-        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*eventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-                .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                           ResyncCallback())));
-
-        auto vsyncController = std::make_unique<mock::VsyncController>();
-        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*vsyncTracker, currentPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                std::move(eventThread), std::move(sfEventThread));
-    }
-
     TestableSurfaceFlinger mFlinger;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
 
     FenceToFenceTimeMap fenceFactory;
 
     void PresentedSurfaceFrameForBufferlessTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
+        sp<Layer> layer = createLayer();
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
         EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_TRUE(layer->mDrawingState.bufferSurfaceFrameTX == nullptr);
         const auto surfaceFrame = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
@@ -112,8 +86,8 @@
     }
 
     void PresentedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        sp<Fence> fence(new Fence());
+        sp<Layer> layer = createLayer();
+        sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
         bufferData.acquireFence = fence;
@@ -125,8 +99,10 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
         acquireFence->signalForTest(12);
 
         commitTransaction(layer.get());
@@ -136,8 +112,7 @@
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
 
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         EXPECT_EQ(1, surfaceFrame->getToken());
         EXPECT_EQ(true, surfaceFrame->getIsBuffer());
@@ -145,9 +120,9 @@
     }
 
     void DroppedSurfaceFrameForBufferTransaction() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
-        sp<Fence> fence1(new Fence());
+        sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         BufferData bufferData;
         bufferData.acquireFence = fence1;
@@ -159,13 +134,15 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
-        sp<Fence> fence2(new Fence());
+        sp<Fence> fence2(sp<Fence>::make());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
         nsecs_t start = systemTime();
         bufferData.acquireFence = fence2;
@@ -177,8 +154,7 @@
                                                          2ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
         nsecs_t end = systemTime();
         acquireFence2->signalForTest(12);
 
@@ -187,8 +163,7 @@
         const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         commitTransaction(layer.get());
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         EXPECT_EQ(1, droppedSurfaceFrame->getToken());
         EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
@@ -203,15 +178,17 @@
     }
 
     void BufferlessSurfaceFramePromotedToBufferSurfaceFrame() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
 
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
+        layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
 
         EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
 
-        sp<Fence> fence(new Fence());
+        sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
         bufferData.acquireFence = fence;
@@ -223,8 +200,7 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
         acquireFence->signalForTest(12);
 
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -237,15 +213,14 @@
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
 
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
     }
 
     void BufferlessSurfaceFrameNotCreatedIfBufferSufaceFrameExists() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        sp<Fence> fence(new Fence());
+        sp<Layer> layer = createLayer();
+        sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
         bufferData.acquireFence = fence;
@@ -257,33 +232,38 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
 
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
+        layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
     }
 
     void MultipleSurfaceFramesPresentedTogether() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 1, /*inputEventId*/ 0},
-                                                             10);
+        sp<Layer> layer = createLayer();
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
         EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto bufferlessSurfaceFrame1 =
                 layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
 
-        layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 4, /*inputEventId*/ 0},
-                                                             10);
+        FrameTimelineInfo ftInfo2;
+        ftInfo2.vsyncId = 4;
+        ftInfo2.inputEventId = 0;
+        layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
         EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto bufferlessSurfaceFrame2 = layer->mDrawingState.bufferlessSurfaceFramesTX[4];
 
-        sp<Fence> fence(new Fence());
+        sp<Fence> fence(sp<Fence>::make());
         auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
         BufferData bufferData;
         bufferData.acquireFence = fence;
@@ -295,8 +275,10 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 3, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo3;
+        ftInfo3.vsyncId = 3;
+        ftInfo3.inputEventId = 0;
+        layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
         EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -318,16 +300,15 @@
         // Buffers are presented only at latch time.
         EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
 
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         EXPECT_EQ(PresentState::Presented, bufferSurfaceFrameTX->getPresentState());
     }
 
     void PendingSurfaceFramesRemovedAfterClassification() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
-        sp<Fence> fence1(new Fence());
+        sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         BufferData bufferData;
         bufferData.acquireFence = fence1;
@@ -339,12 +320,14 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
-        sp<Fence> fence2(new Fence());
+        sp<Fence> fence2(sp<Fence>::make());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
         bufferData.acquireFence = fence2;
         bufferData.frameNumber = 1;
@@ -355,16 +338,14 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
         acquireFence2->signalForTest(12);
 
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         commitTransaction(layer.get());
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
         // pendingJankClassifications.
@@ -377,9 +358,9 @@
     }
 
     void BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
 
-        sp<Fence> fence1(new Fence());
+        sp<Fence> fence1(sp<Fence>::make());
         auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
         BufferData bufferData;
         bufferData.acquireFence = fence1;
@@ -391,13 +372,15 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 1, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo;
+        ftInfo.vsyncId = 1;
+        ftInfo.inputEventId = 0;
+        layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
 
-        sp<Fence> fence2(new Fence());
+        sp<Fence> fence2(sp<Fence>::make());
         auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
         auto dropStartTime1 = systemTime();
         bufferData.acquireFence = fence2;
@@ -409,14 +392,16 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfoInv;
+        ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
+        ftInfoInv.inputEventId = 0;
+        layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
         auto dropEndTime1 = systemTime();
         EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
         ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
         const auto droppedSurfaceFrame2 = layer->mDrawingState.bufferSurfaceFrameTX;
 
-        sp<Fence> fence3(new Fence());
+        sp<Fence> fence3(sp<Fence>::make());
         auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
         auto dropStartTime2 = systemTime();
         bufferData.acquireFence = fence3;
@@ -428,8 +413,10 @@
                                                          1ULL /* bufferId */,
                                                          HAL_PIXEL_FORMAT_RGBA_8888,
                                                          0ULL /*usage*/);
-        layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt,
-                         {/*vsyncId*/ 2, /*inputEventId*/ 0});
+        FrameTimelineInfo ftInfo2;
+        ftInfo2.vsyncId = 2;
+        ftInfo2.inputEventId = 0;
+        layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
         auto dropEndTime2 = systemTime();
         acquireFence3->signalForTest(12);
 
@@ -438,8 +425,7 @@
         const auto presentedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
 
         commitTransaction(layer.get());
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
 
         EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
         EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
@@ -461,11 +447,11 @@
     }
 
     void MultipleCommitsBeforeLatch() {
-        sp<BufferStateLayer> layer = createBufferStateLayer();
+        sp<Layer> layer = createLayer();
         uint32_t surfaceFramesPendingClassification = 0;
         std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
         for (int i = 0; i < 10; i += 2) {
-            sp<Fence> fence(new Fence());
+            sp<Fence> fence(sp<Fence>::make());
             BufferData bufferData;
             bufferData.acquireFence = fence;
             bufferData.frameNumber = 1;
@@ -476,11 +462,14 @@
                                                              1ULL /* bufferId */,
                                                              HAL_PIXEL_FORMAT_RGBA_8888,
                                                              0ULL /*usage*/);
-            layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt,
-                             {/*vsyncId*/ 1, /*inputEventId*/ 0});
-            layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
-                                                                  /*inputEventId*/ 0},
-                                                                 10);
+            FrameTimelineInfo ftInfo;
+            ftInfo.vsyncId = 1;
+            ftInfo.inputEventId = 0;
+            layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+            FrameTimelineInfo ftInfo2;
+            ftInfo2.vsyncId = 2;
+            ftInfo2.inputEventId = 0;
+            layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
             ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
             EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
             auto& bufferlessSurfaceFrame =
@@ -494,8 +483,7 @@
         }
 
         auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
-        bool computeVisisbleRegions;
-        layer->updateTexImage(computeVisisbleRegions, 15, 0);
+        layer->updateTexImage(15);
         // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
         // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
         for (auto& surfaceFrame : bufferlessSurfaceFrames) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 61b72a0..92411a7 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -18,7 +18,13 @@
 #include <gtest/gtest.h>
 
 #include <gui/SurfaceComposerClient.h>
+#include <cstdint>
+#include "Client.h"
 
+#include <layerproto/LayerProtoHeader.h>
+#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/Update.h"
+#include "Tracing/LayerTracing.h"
 #include "Tracing/RingBuffer.h"
 #include "Tracing/TransactionTracing.h"
 
@@ -42,14 +48,15 @@
     }
 
     void queueAndCommitTransaction(int64_t vsyncId) {
+        frontend::Update update;
         TransactionState transaction;
         transaction.id = static_cast<uint64_t>(vsyncId * 3);
         transaction.originUid = 1;
         transaction.originPid = 2;
         mTracing.addQueuedTransaction(transaction);
         std::vector<TransactionState> transactions;
-        transactions.emplace_back(transaction);
-        mTracing.addCommittedTransactions(transactions, vsyncId);
+        update.transactions.emplace_back(transaction);
+        mTracing.addCommittedTransactions(vsyncId, 0, update, {}, false);
         flush(vsyncId);
     }
 
@@ -57,13 +64,31 @@
                      const std::vector<TransactionState>& expectedTransactions,
                      int64_t expectedVsyncId) {
         EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
-        EXPECT_EQ(actualProto.transactions().size(),
+        ASSERT_EQ(actualProto.transactions().size(),
                   static_cast<int32_t>(expectedTransactions.size()));
         for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
-            EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
-                      expectedTransactions[i].originPid);
+            const auto expectedTransaction = expectedTransactions[i];
+            const auto protoTransaction = actualProto.transactions(static_cast<int32_t>(i));
+            EXPECT_EQ(protoTransaction.transaction_id(), expectedTransaction.id);
+            EXPECT_EQ(protoTransaction.pid(), expectedTransaction.originPid);
+            for (uint32_t i = 0; i < expectedTransaction.mergedTransactionIds.size(); i++) {
+                EXPECT_EQ(protoTransaction.merged_transaction_ids(static_cast<int32_t>(i)),
+                          expectedTransaction.mergedTransactionIds[i]);
+            }
         }
     }
+
+    LayerCreationArgs getLayerCreationArgs(uint32_t layerId, uint32_t parentId,
+                                           uint32_t layerIdToMirror, uint32_t flags,
+                                           bool addToRoot) {
+        LayerCreationArgs args;
+        args.sequence = layerId;
+        args.parentId = parentId;
+        args.layerIdToMirror = layerIdToMirror;
+        args.flags = flags;
+        args.addToRoot = addToRoot;
+        return args;
+    }
 };
 
 TEST_F(TransactionTracingTest, addTransactions) {
@@ -73,61 +98,66 @@
         TransactionState transaction;
         transaction.id = i;
         transaction.originPid = static_cast<int32_t>(i);
+        transaction.mergedTransactionIds = std::vector<uint64_t>{i + 100, i + 102};
         transactions.emplace_back(transaction);
         mTracing.addQueuedTransaction(transaction);
     }
 
     // Split incoming transactions into two and commit them in reverse order to test out of order
     // commits.
-    std::vector<TransactionState> firstTransactionSet =
-            std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
     int64_t firstTransactionSetVsyncId = 42;
-    mTracing.addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+    frontend::Update firstUpdate;
+    firstUpdate.transactions =
+            std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+    mTracing.addCommittedTransactions(firstTransactionSetVsyncId, 0, firstUpdate, {}, false);
 
     int64_t secondTransactionSetVsyncId = 43;
-    std::vector<TransactionState> secondTransactionSet =
+    frontend::Update secondUpdate;
+    secondUpdate.transactions =
             std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
-    mTracing.addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+    mTracing.addCommittedTransactions(secondTransactionSetVsyncId, 0, secondUpdate, {}, false);
     flush(secondTransactionSetVsyncId);
 
     proto::TransactionTraceFile proto = writeToProto();
-    EXPECT_EQ(proto.entry().size(), 2);
-    verifyEntry(proto.entry(0), firstTransactionSet, firstTransactionSetVsyncId);
-    verifyEntry(proto.entry(1), secondTransactionSet, secondTransactionSetVsyncId);
+    ASSERT_EQ(proto.entry().size(), 2);
+    verifyEntry(proto.entry(0), firstUpdate.transactions, firstTransactionSetVsyncId);
+    verifyEntry(proto.entry(1), secondUpdate.transactions, secondTransactionSetVsyncId);
 }
 
 class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        // add layers
         mTracing.setBufferSize(SMALL_BUFFER_SIZE);
-        const sp<IBinder> fakeLayerHandle = new BBinder();
-        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
-                              123 /* flags */, -1 /* parentId */);
-        const sp<IBinder> fakeChildLayerHandle = new BBinder();
-        mTracing.onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
-                              456 /* flags */, mParentLayerId);
 
-        // add some layer transaction
+        // add layers and add some layer transaction
         {
+            frontend::Update update;
+            update.layerCreationArgs.emplace_back(std::move(
+                    getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+                                         /*addToRoot=*/true)));
+            update.layerCreationArgs.emplace_back(std::move(
+                    getLayerCreationArgs(mChildLayerId, mParentLayerId,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
+                                         /*addToRoot=*/true)));
             TransactionState transaction;
             transaction.id = 50;
-            ComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            ResolvedComposerState layerState;
+            layerState.layerId = mParentLayerId;
             layerState.state.what = layer_state_t::eLayerChanged;
             layerState.state.z = 42;
-            transaction.states.add(layerState);
-            ComposerState childState;
-            childState.state.surface = fakeChildLayerHandle;
+            transaction.states.emplace_back(layerState);
+            ResolvedComposerState childState;
+            childState.layerId = mChildLayerId;
             childState.state.what = layer_state_t::eLayerChanged;
             childState.state.z = 43;
-            transaction.states.add(childState);
+            transaction.states.emplace_back(childState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
+            update.transactions.emplace_back(transaction);
             VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
-            mTracing.addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(VSYNC_ID_FIRST_LAYER_CHANGE, 0, update, {}, false);
+
             flush(VSYNC_ID_FIRST_LAYER_CHANGE);
         }
 
@@ -136,18 +166,18 @@
         {
             TransactionState transaction;
             transaction.id = 51;
-            ComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            ResolvedComposerState layerState;
+            layerState.layerId = mParentLayerId;
             layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
             layerState.state.z = 41;
             layerState.state.x = 22;
-            transaction.states.add(layerState);
+            transaction.states.emplace_back(layerState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
+            frontend::Update update;
+            update.transactions.emplace_back(transaction);
             VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
-            mTracing.addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+            mTracing.addCommittedTransactions(VSYNC_ID_SECOND_LAYER_CHANGE, 0, update, {}, false);
             flush(VSYNC_ID_SECOND_LAYER_CHANGE);
         }
 
@@ -161,8 +191,8 @@
         queueAndCommitTransaction(++mVsyncId);
     }
 
-    int mParentLayerId = 1;
-    int mChildLayerId = 2;
+    uint32_t mParentLayerId = 1;
+    uint32_t mChildLayerId = 2;
     int64_t mVsyncId = 0;
     int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
     int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
@@ -230,40 +260,42 @@
 class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
 protected:
     void SetUp() override {
-        // add layers
         mTracing.setBufferSize(SMALL_BUFFER_SIZE);
-        const sp<IBinder> fakeLayerHandle = new BBinder();
-        mTracing.onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
-                              123 /* flags */, -1 /* parentId */);
-        const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
-        mTracing.onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
-                                    mLayerId);
 
-        // add some layer transaction
+        // add layers and some layer transaction
         {
+            frontend::Update update;
+            update.layerCreationArgs.emplace_back(
+                    getLayerCreationArgs(mLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
+                                         /*addToRoot=*/true));
+            update.layerCreationArgs.emplace_back(
+                    getLayerCreationArgs(mMirrorLayerId, UNASSIGNED_LAYER_ID,
+                                         /*layerIdToMirror=*/mLayerId, /*flags=*/0,
+                                         /*addToRoot=*/false));
+
             TransactionState transaction;
             transaction.id = 50;
-            ComposerState layerState;
-            layerState.state.surface = fakeLayerHandle;
+            ResolvedComposerState layerState;
+            layerState.layerId = mLayerId;
             layerState.state.what = layer_state_t::eLayerChanged;
             layerState.state.z = 42;
-            transaction.states.add(layerState);
-            ComposerState mirrorState;
-            mirrorState.state.surface = fakeMirrorLayerHandle;
+            transaction.states.emplace_back(layerState);
+            ResolvedComposerState mirrorState;
+            mirrorState.layerId = mMirrorLayerId;
             mirrorState.state.what = layer_state_t::eLayerChanged;
             mirrorState.state.z = 43;
-            transaction.states.add(mirrorState);
+            transaction.states.emplace_back(mirrorState);
             mTracing.addQueuedTransaction(transaction);
 
-            std::vector<TransactionState> transactions;
-            transactions.emplace_back(transaction);
-            mTracing.addCommittedTransactions(transactions, ++mVsyncId);
+            update.transactions.emplace_back(transaction);
+            mTracing.addCommittedTransactions(mVsyncId, 0, update, {}, false);
             flush(mVsyncId);
         }
     }
 
-    int mLayerId = 5;
-    int mMirrorLayerId = 55;
+    uint32_t mLayerId = 5;
+    uint32_t mMirrorLayerId = 55;
     int64_t mVsyncId = 0;
     int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
     int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
@@ -282,4 +314,24 @@
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
     EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
 }
+
+// Verify we can write the layers traces by entry to reduce mem pressure
+// on the system when generating large traces.
+TEST(LayerTraceTest, canStreamLayersTrace) {
+    LayersTraceFileProto inProto = LayerTracing::createTraceFileProto();
+    inProto.add_entry();
+    inProto.add_entry();
+
+    std::string output;
+    inProto.SerializeToString(&output);
+    LayersTraceFileProto inProto2 = LayerTracing::createTraceFileProto();
+    inProto2.add_entry();
+    std::string output2;
+    inProto2.SerializeToString(&output2);
+
+    LayersTraceFileProto outProto;
+    outProto.ParseFromString(output + output2);
+    // magic?
+    EXPECT_EQ(outProto.entry().size(), 3);
+}
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 15fea9c..108151e 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -22,11 +22,9 @@
 #include <gtest/gtest.h>
 #include <gui/LayerMetadata.h>
 
-#include "BufferStateLayer.h"
 #include "TestableSurfaceFlinger.h"
 #include "TunnelModeEnabledReporter.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockEventThread.h"
 
 namespace android {
 
@@ -37,8 +35,6 @@
 using android::Hwc2::IComposer;
 using android::Hwc2::IComposerClient;
 
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-
 constexpr int DEFAULT_SIDEBAND_STREAM = 51;
 
 struct TestableTunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener {
@@ -62,9 +58,7 @@
     static constexpr uint32_t HEIGHT = 100;
     static constexpr uint32_t LAYER_FLAGS = 0;
 
-    void setupScheduler();
-    void setupComposer(uint32_t virtualDisplayCount);
-    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+    sp<Layer> createBufferStateLayer(LayerMetadata metadata);
 
     TestableSurfaceFlinger mFlinger;
     Hwc2::mock::Composer* mComposer = nullptr;
@@ -81,7 +75,7 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    setupScheduler();
+    mFlinger.setupMockScheduler();
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
     mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
     mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
@@ -95,36 +89,10 @@
     mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
 }
 
-sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
-        LayerMetadata metadata = {}) {
+sp<Layer> TunnelModeEnabledReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
     sp<Client> client;
     LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
-    return new BufferStateLayer(args);
-}
-
-void TunnelModeEnabledReporterTest::setupScheduler() {
-    auto eventThread = std::make_unique<mock::EventThread>();
-    auto sfEventThread = std::make_unique<mock::EventThread>();
-
-    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*eventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
-            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
-                                                       ResyncCallback())));
-
-    auto vsyncController = std::make_unique<mock::VsyncController>();
-    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
-
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*vsyncTracker, currentPeriod())
-            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
-    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                            std::move(eventThread), std::move(sfEventThread));
+    return sp<Layer>::make(args);
 }
 
 namespace {
diff --git a/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
new file mode 100644
index 0000000..69b3861
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncCallbackRegistrationTest.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "Scheduler/VSyncDispatch.h"
+#include "mock/MockVSyncDispatch.h"
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class VSyncCallbackRegistrationTest : public Test {
+protected:
+    VSyncDispatch::Callback mCallback = [](nsecs_t, nsecs_t, nsecs_t) {};
+
+    std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch = std::make_shared<mock::VSyncDispatch>();
+    VSyncDispatch::CallbackToken mCallbackToken{7};
+    std::string mCallbackName = "callback";
+
+    std::shared_ptr<mock::VSyncDispatch> mVsyncDispatch2 = std::make_shared<mock::VSyncDispatch>();
+    VSyncDispatch::CallbackToken mCallbackToken2{42};
+    std::string mCallbackName2 = "callback2";
+
+    void assertDispatch(const VSyncCallbackRegistration& registration,
+                        std::shared_ptr<VSyncDispatch> dispatch) {
+        ASSERT_EQ(registration.mDispatch, dispatch);
+    }
+
+    void assertToken(const VSyncCallbackRegistration& registration,
+                     const std::optional<VSyncDispatch::CallbackToken>& token) {
+        ASSERT_EQ(registration.mToken, token);
+    }
+};
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnDestruction) {
+    // TODO (b/279581095): With ftl::Function, `_` can be replaced with
+    // `mCallback`, here and in other calls to `registerCallback, since the
+    // ftl version has an operator==, unlike std::function.
+    EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+            .WillOnce(Return(mCallbackToken));
+    EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+    VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnPointerMove) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+                .WillOnce(Return(mCallbackToken));
+        EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+                .WillOnce(Return(mCallbackToken2));
+        EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+        EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+    }
+
+    auto registration =
+            std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch, mCallback, mCallbackName);
+
+    auto registration2 =
+            std::make_unique<VSyncCallbackRegistration>(mVsyncDispatch2, mCallback, mCallbackName2);
+
+    registration2 = std::move(registration);
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(*registration2.get(), mVsyncDispatch));
+    ASSERT_NO_FATAL_FAILURE(assertToken(*registration2.get(), mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, unregistersCallbackOnMoveOperator) {
+    {
+        InSequence seq;
+        EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+                .WillOnce(Return(mCallbackToken));
+        EXPECT_CALL(*mVsyncDispatch2, registerCallback(_, mCallbackName2))
+                .WillOnce(Return(mCallbackToken2));
+        EXPECT_CALL(*mVsyncDispatch2, unregisterCallback(mCallbackToken2)).Times(1);
+        EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+    }
+
+    VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+    VSyncCallbackRegistration registration2(mVsyncDispatch2, mCallback, mCallbackName2);
+
+    registration2 = std::move(registration);
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveConstructor) {
+    EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+            .WillOnce(Return(mCallbackToken));
+    EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+    VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+    VSyncCallbackRegistration registration2(std::move(registration));
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, nullptr));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration, std::nullopt));
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration2, mVsyncDispatch));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration2, mCallbackToken));
+}
+
+TEST_F(VSyncCallbackRegistrationTest, moveOperatorEqualsSelf) {
+    EXPECT_CALL(*mVsyncDispatch, registerCallback(_, mCallbackName))
+            .WillOnce(Return(mCallbackToken));
+    EXPECT_CALL(*mVsyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
+
+    VSyncCallbackRegistration registration(mVsyncDispatch, mCallback, mCallbackName);
+
+    // Use a reference so the compiler doesn't realize that registration is
+    // being moved to itself.
+    VSyncCallbackRegistration& registrationRef = registration;
+    registration = std::move(registrationRef);
+
+    ASSERT_NO_FATAL_FAILURE(assertDispatch(registration, mVsyncDispatch));
+    ASSERT_NO_FATAL_FAILURE(assertToken(registration, mCallbackToken));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2da266b..41866a1 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setRenderRate(Fps) final {}
     void dump(std::string&) const final {}
 
 private:
@@ -91,6 +92,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setRenderRate(Fps) final {}
     void dump(std::string&) const final {}
 
 private:
@@ -107,7 +109,8 @@
 
 class RepeatingCallbackReceiver {
 public:
-    RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration)
+    RepeatingCallbackReceiver(std::shared_ptr<VSyncDispatch> dispatch, nsecs_t workload,
+                              nsecs_t readyDuration)
           : mWorkload(workload),
             mReadyDuration(readyDuration),
             mCallback(
@@ -164,9 +167,10 @@
 };
 
 TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
-    FixedRateIdealStubTracker tracker;
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
-                                     mVsyncMoveThreshold);
+    auto tracker = std::make_shared<FixedRateIdealStubTracker>();
+    auto dispatch =
+            std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+                                                      mDispatchGroupThreshold, mVsyncMoveThreshold);
 
     static size_t constexpr num_clients = 3;
     std::array<RepeatingCallbackReceiver, num_clients>
@@ -193,14 +197,15 @@
 // starts at 333hz, slides down to 43hz
 TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
     auto next_vsync_interval = toNs(3ms);
-    VRRStubTracker tracker(next_vsync_interval);
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
-                                     mVsyncMoveThreshold);
+    auto tracker = std::make_shared<VRRStubTracker>(next_vsync_interval);
+    auto dispatch =
+            std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+                                                      mDispatchGroupThreshold, mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
 
     auto const on_each_frame = [&](nsecs_t last_known) {
-        tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
+        tracker->set_interval(next_vsync_interval += toNs(1ms), last_known);
     };
 
     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
@@ -211,9 +216,10 @@
 
 // starts at 333hz, jumps to 200hz at frame 10
 TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
-    VRRStubTracker tracker(toNs(3ms));
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
-                                     mVsyncMoveThreshold);
+    auto tracker = std::make_shared<VRRStubTracker>(toNs(3ms));
+    auto dispatch =
+            std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+                                                      mDispatchGroupThreshold, mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms));
 
@@ -221,7 +227,7 @@
     auto constexpr jump_frame_at = 10u;
     auto const on_each_frame = [&](nsecs_t last_known) {
         if (jump_frame_counter++ == jump_frame_at) {
-            tracker.set_interval(toNs(5ms), last_known);
+            tracker->set_interval(toNs(5ms), last_known);
         }
     };
     std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b7f968d..7af1da6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -44,6 +44,8 @@
         ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
                 .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
         ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
+        ON_CALL(*this, currentPeriod())
+                .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod));
     }
 
     MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
@@ -53,6 +55,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setRenderRate, (Fps), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
@@ -62,6 +65,8 @@
         return (timePoint - (timePoint % mPeriod) + mPeriod);
     }
 
+    nsecs_t getCurrentPeriod() const { return mPeriod; }
+
 protected:
     nsecs_t const mPeriod;
 };
@@ -111,13 +116,14 @@
 
 class CountingCallback {
 public:
-    CountingCallback(VSyncDispatch& dispatch)
-          : mDispatch(dispatch),
-            mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
-                                                       std::placeholders::_1, std::placeholders::_2,
-                                                       std::placeholders::_3),
-                                             "test")) {}
-    ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+    CountingCallback(std::shared_ptr<VSyncDispatch> dispatch)
+          : mDispatch(std::move(dispatch)),
+            mToken(mDispatch->registerCallback(std::bind(&CountingCallback::counter, this,
+                                                         std::placeholders::_1,
+                                                         std::placeholders::_2,
+                                                         std::placeholders::_3),
+                                               "test")) {}
+    ~CountingCallback() { mDispatch->unregisterCallback(mToken); }
 
     operator VSyncDispatch::CallbackToken() const { return mToken; }
 
@@ -127,7 +133,7 @@
         mReadyTime.push_back(readyTime);
     }
 
-    VSyncDispatch& mDispatch;
+    std::shared_ptr<VSyncDispatch> mDispatch;
     VSyncDispatch::CallbackToken mToken;
     std::vector<nsecs_t> mCalls;
     std::vector<nsecs_t> mWakeupTime;
@@ -136,12 +142,12 @@
 
 class PausingCallback {
 public:
-    PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
-          : mDispatch(dispatch),
-            mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
-                                                       std::placeholders::_1,
-                                                       std::placeholders::_2),
-                                             "test")),
+    PausingCallback(std::shared_ptr<VSyncDispatch> dispatch, std::chrono::milliseconds pauseAmount)
+          : mDispatch(std::move(dispatch)),
+            mToken(mDispatch->registerCallback(std::bind(&PausingCallback::pause, this,
+                                                         std::placeholders::_1,
+                                                         std::placeholders::_2),
+                                               "test")),
             mRegistered(true),
             mPauseAmount(pauseAmount) {}
     ~PausingCallback() { unregister(); }
@@ -176,12 +182,12 @@
 
     void unregister() {
         if (mRegistered) {
-            mDispatch.unregisterCallback(mToken);
+            mDispatch->unregisterCallback(mToken);
             mRegistered = false;
         }
     }
 
-    VSyncDispatch& mDispatch;
+    std::shared_ptr<VSyncDispatch> mDispatch;
     VSyncDispatch::CallbackToken mToken;
     bool mRegistered = true;
 
@@ -226,22 +232,26 @@
     static nsecs_t constexpr mDispatchGroupThreshold = 5;
     nsecs_t const mPeriod = 1000;
     nsecs_t const mVsyncMoveThreshold = 300;
-    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
-    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
-                                      mVsyncMoveThreshold};
+    std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker =
+            std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod);
+    std::shared_ptr<VSyncDispatch> mDispatch =
+            std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker,
+                                                      mDispatchGroupThreshold, mVsyncMoveThreshold);
 };
 
 TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
     {
-        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
-                                          mVsyncMoveThreshold};
+        std::shared_ptr<VSyncDispatch> mDispatch =
+                std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker,
+                                                          mDispatchGroupThreshold,
+                                                          mVsyncMoveThreshold);
         CountingCallback cb(mDispatch);
-        const auto result = mDispatch.schedule(cb,
-                                               {.workDuration = 100,
-                                                .readyDuration = 0,
-                                                .earliestVsync = 1000});
+        const auto result = mDispatch->schedule(cb,
+                                                {.workDuration = 100,
+                                                 .readyDuration = 0,
+                                                 .earliestVsync = 1000});
         EXPECT_TRUE(result.has_value());
         EXPECT_EQ(900, *result);
     }
@@ -252,10 +262,10 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    const auto result = mDispatch.schedule(cb,
-                                           {.workDuration = 100,
-                                            .readyDuration = 0,
-                                            .earliestVsync = intended});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 100,
+                                             .readyDuration = 0,
+                                             .earliestVsync = intended});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(900, *result);
 
@@ -265,12 +275,50 @@
     EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
 }
 
+TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) {
+    auto intended = mPeriod - 230;
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+    auto result = mDispatch->schedule(cb,
+                                      {.workDuration = 100,
+                                       .readyDuration = 0,
+                                       .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
+
+    result =
+            mDispatch->update(cb,
+                              {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(700, *result);
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+    EXPECT_THAT(cb.mWakeupTime[0], Eq(700));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmAt(_, _)).Times(0);
+
+    CountingCallback cb(mDispatch);
+    const auto result =
+            mDispatch->update(cb,
+                              {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+    EXPECT_FALSE(result.has_value());
+}
+
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
     EXPECT_CALL(mMockClock, alarmAt(_, 1050));
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -281,15 +329,15 @@
     auto const now = 234;
     mMockClock.advanceBy(234);
     auto const workDuration = 10 * mPeriod;
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration))
             .WillOnce(Return(mPeriod * 11));
     EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
 
     CountingCallback cb(mDispatch);
-    const auto result = mDispatch.schedule(cb,
-                                           {.workDuration = workDuration,
-                                            .readyDuration = 0,
-                                            .earliestVsync = mPeriod});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = workDuration,
+                                             .readyDuration = 0,
+                                             .earliestVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(mPeriod, *result);
 }
@@ -299,12 +347,13 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    const auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 100,
+                                             .readyDuration = 0,
+                                             .earliestVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(mPeriod - 100, *result);
-    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+    EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
@@ -312,13 +361,14 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    const auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 100,
+                                             .readyDuration = 0,
+                                             .earliestVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(mPeriod - 100, *result);
     mMockClock.advanceBy(950);
-    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+    EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
@@ -326,15 +376,16 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
-    const auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 100,
+                                             .readyDuration = 0,
+                                             .earliestVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
-    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+    EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
     cb.unpause();
     pausingThread.join();
 }
@@ -347,9 +398,10 @@
 
     PausingCallback cb(mDispatch, 50ms);
     cb.stashResource(resource);
-    const auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 100,
+                                             .readyDuration = 0,
+                                             .earliestVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(mPeriod - 100, *result);
 
@@ -366,7 +418,7 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
             .Times(4)
             .WillOnce(Return(1055))
             .WillOnce(Return(1063))
@@ -381,8 +433,8 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
-    mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+    mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
 
     advanceToNextCallback();
     advanceToNextCallback();
@@ -393,8 +445,45 @@
     EXPECT_THAT(cb1.mCalls[0], Eq(1063));
 }
 
+TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) {
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
+            .Times(4)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(2000))
+            .WillOnce(Return(2500))
+            .WillOnce(Return(4000));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 3900)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0});
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(1000));
+
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb.mCalls[1], Eq(2000));
+
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(3));
+    EXPECT_THAT(cb.mCalls[2], Eq(4000));
+}
+
 TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
             .Times(4)
             .WillOnce(Return(10000))
             .WillOnce(Return(1000))
@@ -409,10 +498,10 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0,
-                       {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
-    mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
-    mDispatch.cancel(cb1);
+    mDispatch->schedule(cb0,
+                        {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
+    mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
+    mDispatch->cancel(cb1);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
@@ -423,9 +512,9 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
@@ -438,9 +527,9 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
@@ -458,10 +547,10 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1,
-                       {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1,
+                        {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
     ASSERT_THAT(cb0.mCalls.size(), Eq(1));
@@ -469,9 +558,11 @@
     ASSERT_THAT(cb1.mCalls.size(), Eq(1));
     EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
 
-    mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
-    mDispatch.schedule(cb1,
-                       {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch->schedule(cb1,
+                        {.workDuration = notCloseOffset,
+                         .readyDuration = 0,
+                         .earliestVsync = 2000});
     advanceToNextCallback();
     ASSERT_THAT(cb1.mCalls.size(), Eq(2));
     EXPECT_THAT(cb1.mCalls[1], Eq(2000));
@@ -491,32 +582,32 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+    EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
             .Times(3)
             .WillOnce(Return(950))
             .WillOnce(Return(1975))
             .WillOnce(Return(2950));
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
 
     mMockClock.advanceBy(850);
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
 
-    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
     mMockClock.advanceBy(900);
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
     mMockClock.advanceBy(125);
     EXPECT_THAT(cb.mCalls.size(), Eq(2));
 
-    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
     mMockClock.advanceBy(975);
     EXPECT_THAT(cb.mCalls.size(), Eq(3));
 }
@@ -527,48 +618,48 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
 
     VSyncDispatch::CallbackToken tmp;
-    tmp = mDispatch.registerCallback(
+    tmp = mDispatch->registerCallback(
             [&](auto, auto, auto) {
-                mDispatch.schedule(tmp,
-                                   {.workDuration = 100,
-                                    .readyDuration = 0,
-                                    .earliestVsync = 2000});
+                mDispatch->schedule(tmp,
+                                    {.workDuration = 100,
+                                     .readyDuration = 0,
+                                     .earliestVsync = 2000});
             },
             "o.o");
 
-    mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
     VSyncDispatch::CallbackToken tmp;
     std::optional<nsecs_t> lastTarget;
-    tmp = mDispatch.registerCallback(
+    tmp = mDispatch->registerCallback(
             [&](auto timestamp, auto, auto) {
                 auto result =
-                        mDispatch.schedule(tmp,
-                                           {.workDuration = 400,
-                                            .readyDuration = 0,
-                                            .earliestVsync = timestamp - mVsyncMoveThreshold});
-                EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
-                result = mDispatch.schedule(tmp,
+                        mDispatch->schedule(tmp,
                                             {.workDuration = 400,
                                              .readyDuration = 0,
-                                             .earliestVsync = timestamp});
+                                             .earliestVsync = timestamp - mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
                 EXPECT_EQ(mPeriod + timestamp - 400, *result);
-                result = mDispatch.schedule(tmp,
-                                            {.workDuration = 400,
-                                             .readyDuration = 0,
-                                             .earliestVsync = timestamp + mVsyncMoveThreshold});
+                result = mDispatch->schedule(tmp,
+                                             {.workDuration = 400,
+                                              .readyDuration = 0,
+                                              .earliestVsync = timestamp});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch->schedule(tmp,
+                                             {.workDuration = 400,
+                                              .readyDuration = 0,
+                                              .earliestVsync = timestamp + mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
                 EXPECT_EQ(mPeriod + timestamp - 400, *result);
                 lastTarget = timestamp;
             },
             "oo");
 
-    mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
     advanceToNextCallback();
     EXPECT_THAT(lastTarget, Eq(1000));
 
@@ -584,16 +675,16 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
 
     CountingCallback cb(mDispatch);
-    mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
 
     mMockClock.advanceBy(750);
-    mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
-    mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
 
     mMockClock.advanceBy(800);
-    mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
@@ -606,12 +697,12 @@
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
 
-    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
-    mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
-    mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
+    mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
     advanceToNextCallback();
@@ -623,8 +714,8 @@
 
     CountingCallback cb0(mDispatch);
     CountingCallback cb1(mDispatch);
-    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
+    mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
@@ -634,29 +725,30 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
 
     CountingCallback cb0(mDispatch);
-    mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.cancel(cb0);
-    mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->cancel(cb0);
+    mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
-    EXPECT_FALSE(mDispatch
-                         .schedule(token,
-                                   {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
-                         .has_value());
-    EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+    EXPECT_FALSE(
+            mDispatch
+                    ->schedule(token,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+                    .has_value());
+    EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
     auto result =
-            mDispatch.schedule(cb0,
-                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb0,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(500, *result);
-    result = mDispatch.schedule(cb0,
-                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    result = mDispatch->schedule(cb0,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(900, *result);
 }
@@ -666,14 +758,14 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 500));
     CountingCallback cb(mDispatch);
     auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
 
-    result = mDispatch.schedule(cb,
-                                {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1200, *result);
     advanceToNextCallback();
@@ -681,19 +773,19 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
             .Times(2)
             .WillOnce(Return(1000))
             .WillOnce(Return(1002));
     CountingCallback cb(mDispatch);
     auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
-    result = mDispatch.schedule(cb,
-                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(602, *result);
 }
@@ -701,13 +793,13 @@
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
     CountingCallback cb0(mDispatch);
     auto result =
-            mDispatch.schedule(cb0,
-                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb0,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    result = mDispatch.schedule(cb0,
-                                {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb0,
+                                 {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(900, *result);
 }
@@ -718,13 +810,13 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
     CountingCallback cb0(mDispatch);
     auto result =
-            mDispatch.schedule(cb0,
-                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb0,
+                                {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    result = mDispatch.schedule(cb0,
-                                {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb0,
+                                 {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1100, *result);
 }
@@ -734,13 +826,13 @@
 
     CountingCallback cb(mDispatch);
     auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
 
-    result = mDispatch.schedule(cb,
-                                {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
 
@@ -786,16 +878,16 @@
     CountingCallback cb2(mDispatch);
 
     auto result =
-            mDispatch.schedule(cb1,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb1,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    result = mDispatch.schedule(cb2,
-                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1900, *result);
     mMockClock.advanceBy(80);
@@ -814,16 +906,16 @@
     CountingCallback cb(mDispatch);
 
     auto result =
-            mDispatch.schedule(cb,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    result = mDispatch.schedule(cb,
-                                {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb,
+                                 {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1630, *result);
     mMockClock.advanceBy(80);
@@ -840,19 +932,19 @@
     CountingCallback cb2(mDispatch);
 
     auto result =
-            mDispatch.schedule(cb1,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb1,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
-    result = mDispatch.schedule(cb2,
-                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+    EXPECT_EQ(mDispatch->cancel(cb2), CancelResult::Cancelled);
 
     mMockClock.advanceBy(80);
 
@@ -869,19 +961,19 @@
     CountingCallback cb2(mDispatch);
 
     auto result =
-            mDispatch.schedule(cb1,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb1,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
-    result = mDispatch.schedule(cb2,
-                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    result = mDispatch->schedule(cb2,
+                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+    EXPECT_EQ(mDispatch->cancel(cb1), CancelResult::Cancelled);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(0));
     EXPECT_THAT(cb2.mCalls.size(), Eq(0));
@@ -896,21 +988,21 @@
     CountingCallback cb2(mDispatch);
 
     Sequence seq;
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
             .InSequence(seq)
             .WillOnce(Return(1000));
     EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000))
             .InSequence(seq)
             .WillOnce(Return(1000));
 
     auto result =
-            mDispatch.schedule(cb1,
-                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+            mDispatch->schedule(cb1,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(600, *result);
-    result = mDispatch.schedule(cb2,
-                                {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+    result = mDispatch->schedule(cb2,
+                                 {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(610, *result);
 
@@ -932,10 +1024,10 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    const auto result = mDispatch.schedule(cb,
-                                           {.workDuration = 70,
-                                            .readyDuration = 30,
-                                            .earliestVsync = intended});
+    const auto result = mDispatch->schedule(cb,
+                                            {.workDuration = 70,
+                                             .readyDuration = 30,
+                                             .earliestVsync = intended});
     EXPECT_TRUE(result.has_value());
     EXPECT_EQ(900, *result);
     advanceToNextCallback();
@@ -954,8 +1046,8 @@
 
     CountingCallback cb(mDispatch);
 
-    mDispatch.schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
-    mDispatch.schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
 
     advanceToNextCallback();
 
@@ -973,7 +1065,8 @@
 protected:
     nsecs_t const mPeriod = 1000;
     nsecs_t const mVsyncMoveThreshold = 200;
-    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+    std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker =
+            std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod);
 };
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
@@ -991,7 +1084,7 @@
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
@@ -1005,7 +1098,7 @@
     auto const duration = 500;
     auto const now = 8750;
 
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration))
             .Times(1)
             .WillOnce(Return(10000));
     VSyncDispatchTimerQueueEntry entry(
@@ -1013,7 +1106,7 @@
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
-                               mStubTracker, now)
+                               *mStubTracker.get(), now)
                         .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
@@ -1036,7 +1129,7 @@
             mVsyncMoveThreshold);
 
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
@@ -1058,7 +1151,7 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_))
             .Times(2)
             .WillOnce(Return(1000))
             .WillOnce(Return(1020));
@@ -1067,17 +1160,17 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    entry.update(mStubTracker, 0);
+    entry.update(*mStubTracker.get(), 0);
     EXPECT_FALSE(entry.wakeupTime());
 
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
 
-    entry.update(mStubTracker, 0);
+    entry.update(*mStubTracker.get(), 0);
     wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(920));
@@ -1087,9 +1180,9 @@
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
-    entry.update(mStubTracker, 0);
+    entry.update(*mStubTracker.get(), 0);
 
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
@@ -1100,24 +1193,24 @@
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
     EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
     EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
     EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
@@ -1129,24 +1222,24 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     Sequence seq;
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
             .InSequence(seq)
             .WillOnce(Return(1000));
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500))
             .InSequence(seq)
             .WillOnce(Return(1000));
-    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+    EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
             .InSequence(seq)
             .WillOnce(Return(2000));
 
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
 
     entry.executing(); // 1000 is executing
 
     EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
 }
 
@@ -1154,16 +1247,16 @@
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
 }
 
@@ -1176,7 +1269,7 @@
     entry.addPendingWorkloadUpdate(
             {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
     EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
-    entry.update(mStubTracker, 0);
+    entry.update(*mStubTracker.get(), 0);
     EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
     EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
 }
@@ -1197,7 +1290,7 @@
             mVsyncMoveThreshold);
 
     EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
-                               mStubTracker, 0)
+                               *mStubTracker.get(), 0)
                         .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 74d2b7d..43d683d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -47,6 +47,8 @@
     return vsyncs;
 }
 
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
 struct VSyncPredictorTest : testing::Test {
     nsecs_t mNow = 0;
     nsecs_t mPeriod = 1000;
@@ -55,7 +57,7 @@
     static constexpr size_t kOutlierTolerancePercent = 25;
     static constexpr nsecs_t mMaxRoundingError = 100;
 
-    VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction,
+    VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction,
                            kOutlierTolerancePercent};
 };
 
@@ -376,7 +378,8 @@
 
 // See b/151146131
 TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
-    VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+    VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction,
+                           kOutlierTolerancePercent};
     std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
                                                840923581635, 840940161584, 840956868096,
                                                840973702473, 840990256277, 841007116851,
@@ -468,7 +471,7 @@
     const auto maxPeriods = 15;
     for (int divisor = 1; divisor < maxDivisor; divisor++) {
         for (int i = 0; i < maxPeriods; i++) {
-            const bool expectedInPhase = (i % divisor) == 0;
+            const bool expectedInPhase = ((kMinimumSamplesForPrediction - 1 + i) % divisor) == 0;
             EXPECT_THAT(expectedInPhase,
                         tracker.isVSyncInPhase(mNow + i * mPeriod - bias,
                                                Fps::fromPeriodNsecs(divisor * mPeriod)))
@@ -478,6 +481,28 @@
     }
 }
 
+TEST_F(VSyncPredictorTest, isVSyncInPhaseForDivisors) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 1 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 2)));
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 2 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 2)));
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 3 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 2)));
+
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 5 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 4)));
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 3 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 4)));
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 4 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 4)));
+    EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 6 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 4)));
+    EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 7 * mPeriod, Fps::fromPeriodNsecs(mPeriod * 4)));
+}
+
 TEST_F(VSyncPredictorTest, inconsistentVsyncValueIsFlushedEventually) {
     EXPECT_TRUE(tracker.addVsyncTimestamp(600));
     EXPECT_TRUE(tracker.needsMoreSamples());
@@ -532,6 +557,75 @@
     EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
 }
 
+TEST_F(VSyncPredictorTest, setRenderRateIsRespected) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
+
+    tracker.setRenderRate(refreshRate / 4);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod));
+
+    tracker.setRenderRate(refreshRate / 2);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5 * mPeriod), Eq(mNow + 7 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod));
+
+    tracker.setRenderRate(refreshRate / 6);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod));
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 2 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 5 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 30a3f9a..122192b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,6 +50,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setRenderRate, (Fps), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
@@ -68,21 +69,13 @@
     std::shared_ptr<Clock> const mClock;
 };
 
-struct MockVSyncDispatch : VSyncDispatch {
-    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
-    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(CancelResult, cancel, (CallbackToken), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-};
-
 std::shared_ptr<android::FenceTime> generateInvalidFence() {
-    sp<Fence> fence = new Fence();
+    sp<Fence> fence = sp<Fence>::make();
     return std::make_shared<android::FenceTime>(fence);
 }
 
 std::shared_ptr<android::FenceTime> generatePendingFence() {
-    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    sp<Fence> fence = sp<Fence>::make(dup(fileno(tmpfile())));
     return std::make_shared<android::FenceTime>(fence);
 }
 
@@ -92,19 +85,21 @@
 }
 
 std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
-    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    sp<Fence> fence = sp<Fence>::make(dup(fileno(tmpfile())));
     std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence);
     signalFenceWithTime(ft, time);
     return ft;
 }
 
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
 class VSyncReactorTest : public testing::Test {
 protected:
     VSyncReactorTest()
           : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
             mMockClock(std::make_shared<NiceMock<MockClock>>()),
-            mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit,
-                     false /* supportKernelIdleTimer */) {
+            mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
+                     kPendingLimit, false /* supportKernelIdleTimer */) {
         ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
         ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
     }
@@ -199,7 +194,7 @@
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
@@ -212,7 +207,7 @@
 TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     bool periodFlushed = true;
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
@@ -231,7 +226,7 @@
 TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
     nsecs_t sampleTime = 0;
     nsecs_t const newPeriod = 5000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
     bool periodFlushed = true;
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
@@ -239,7 +234,7 @@
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    mReactor.startPeriodTransition(period);
+    mReactor.startPeriodTransition(period, false);
     EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
@@ -249,13 +244,13 @@
     nsecs_t const secondPeriod = 5000;
     nsecs_t const thirdPeriod = 2000;
 
-    mReactor.startPeriodTransition(secondPeriod);
+    mReactor.startPeriodTransition(secondPeriod, false);
     bool periodFlushed = true;
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    mReactor.startPeriodTransition(thirdPeriod);
+    mReactor.startPeriodTransition(thirdPeriod, false);
     EXPECT_TRUE(
             mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
@@ -296,14 +291,14 @@
 
 TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
     nsecs_t const newPeriod = 5000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
 TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     bool periodFlushed = true;
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
@@ -328,7 +323,7 @@
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
 
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     auto time = 0;
     auto constexpr numTimestampSubmissions = 10;
@@ -353,7 +348,7 @@
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
 
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     auto time = 0;
     // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
@@ -370,7 +365,7 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     time += period;
     mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
@@ -386,7 +381,7 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     static auto constexpr numSamplesWithNewPeriod = 4;
     Sequence seq;
@@ -413,7 +408,7 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     Sequence seq;
     EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -433,7 +428,7 @@
     nsecs_t const newPeriod1 = 4000;
     nsecs_t const newPeriod2 = 7000;
 
-    mReactor.startPeriodTransition(newPeriod1);
+    mReactor.startPeriodTransition(newPeriod1, false);
 
     Sequence seq;
     EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -452,7 +447,7 @@
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
 
-    mReactor.startPeriodTransition(newPeriod2);
+    mReactor.startPeriodTransition(newPeriod2, false);
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
@@ -465,7 +460,7 @@
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.startPeriodTransition(newPeriod);
+    mReactor.startPeriodTransition(newPeriod, false);
 
     EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
@@ -479,8 +474,9 @@
 
 TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
     // Create a reactor which supports the kernel idle timer
-    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker,
-                                    kPendingLimit, true /* supportKernelIdleTimer */);
+    auto idleReactor =
+            VSyncReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock),
+                         *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */);
 
     bool periodFlushed = true;
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
@@ -488,7 +484,7 @@
 
     // First, set the same period, which should only be confirmed when we receive two
     // matching callbacks
-    idleReactor.startPeriodTransition(10000);
+    idleReactor.startPeriodTransition(10000, false);
     EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     // Correct period but incorrect timestamp delta
@@ -501,7 +497,7 @@
     // Then, set a new period, which should be confirmed as soon as we receive a callback
     // reporting the new period
     nsecs_t const newPeriod = 5000;
-    idleReactor.startPeriodTransition(newPeriod);
+    idleReactor.startPeriodTransition(newPeriod, false);
     // Incorrect timestamp delta and period
     EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index b519582..8acbd6f 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -57,17 +57,14 @@
 
     using Schedule = scheduler::TransactionSchedule;
     using nanos = std::chrono::nanoseconds;
-    const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
-                                             nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
-    const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
-                                                nanos(SF_DURATION_EARLY),
-                                                nanos(APP_DURATION_EARLY)};
-    const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
-                                            nanos(SF_DURATION_EARLY_GPU),
-                                            nanos(APP_DURATION_EARLY_GPU)};
+    const VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+                             nanos(APP_DURATION_LATE)};
+    const VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, nanos(SF_DURATION_EARLY),
+                                nanos(APP_DURATION_EARLY)};
+    const VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+                            nanos(APP_DURATION_EARLY_GPU)};
 
-    const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate,
-                                                     nanos(HWC_MIN_WORK_DURATION)};
+    const VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate, nanos(HWC_MIN_WORK_DURATION)};
     sp<TestableVsyncModulator> mVsyncModulator = sp<TestableVsyncModulator>::make(mOffsets, Now);
 
     void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator->setVsyncConfigSet(mOffsets)); }
diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
new file mode 100644
index 0000000..4010fa6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <scheduler/Fps.h>
+#include "Scheduler/VsyncSchedule.h"
+#include "ThreadContext.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
+
+using testing::_;
+
+namespace android {
+
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
+
+class VsyncScheduleTest : public testing::Test {
+protected:
+    VsyncScheduleTest();
+    ~VsyncScheduleTest() override;
+
+    scheduler::mock::SchedulerCallback mCallback;
+    const std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule =
+            std::unique_ptr<scheduler::VsyncSchedule>(
+                    new scheduler::VsyncSchedule(DEFAULT_DISPLAY_ID,
+                                                 std::make_shared<mock::VSyncTracker>(),
+                                                 std::make_shared<mock::VSyncDispatch>(),
+                                                 std::make_unique<mock::VsyncController>()));
+
+    mock::VsyncController& getController() {
+        return *static_cast<mock::VsyncController*>(&mVsyncSchedule->getController());
+    }
+};
+
+VsyncScheduleTest::VsyncScheduleTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+VsyncScheduleTest::~VsyncScheduleTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+namespace {
+
+using namespace testing;
+
+TEST_F(VsyncScheduleTest, InitiallyDisallowed) {
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDoesNothingWhenDisallowed) {
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed) {
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) {
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, MakeAllowed) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, EnableWorksOnce) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+}
+
+TEST_F(VsyncScheduleTest, AllowedIsSticky) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, EnableDisable) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, EnableDisable2) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransition) {
+    // Note: startPeriodTransition is only called when hardware vsyncs are
+    // allowed.
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+
+    const Period period = (60_Hz).getPeriod();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+
+    mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    const Period period = (60_Hz).getPeriod();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false));
+
+    mVsyncSchedule->startPeriodTransition(mCallback, period, false);
+}
+
+TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+
+    const Period period = (60_Hz).getPeriod();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true));
+    EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true));
+
+    mVsyncSchedule->startPeriodTransition(mCallback, period, true);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) {
+    const Period period = (60_Hz).getPeriod();
+    const auto timestamp = TimePoint::now();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleDisabled) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    const Period period = (60_Hz).getPeriod();
+    const auto timestamp = TimePoint::now();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0);
+
+    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsTrue) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    const Period period = (60_Hz).getPeriod();
+    const auto timestamp = TimePoint::now();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0);
+    EXPECT_CALL(getController(),
+                addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+            .WillOnce(Return(true));
+
+    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, AddResyncSampleReturnsFalse) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->enableHardwareVsync(mCallback);
+
+    const Period period = (60_Hz).getPeriod();
+    const auto timestamp = TimePoint::now();
+
+    EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false));
+    EXPECT_CALL(getController(),
+                addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _))
+            .WillOnce(Return(false));
+
+    mVsyncSchedule->addResyncSample(mCallback, timestamp, period);
+}
+
+TEST_F(VsyncScheduleTest, PendingState) FTL_FAKE_GUARD(kMainThreadContext) {
+    ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+    mVsyncSchedule->setPendingHardwareVsyncState(true);
+    ASSERT_TRUE(mVsyncSchedule->getPendingHardwareVsyncState());
+
+    mVsyncSchedule->setPendingHardwareVsyncState(false);
+    ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState());
+}
+
+TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) {
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */);
+    ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) {
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */));
+    mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */);
+    ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
new file mode 100644
index 0000000..af4971b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/WindowInfosListenerInvokerTest.cpp
@@ -0,0 +1,244 @@
+#include <android/gui/BnWindowInfosListener.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/WindowInfosUpdate.h>
+#include <condition_variable>
+
+#include "BackgroundExecutor.h"
+#include "WindowInfosListenerInvoker.h"
+#include "android/gui/IWindowInfosReportedListener.h"
+
+namespace android {
+
+class WindowInfosListenerInvokerTest : public testing::Test {
+protected:
+    WindowInfosListenerInvokerTest() : mInvoker(sp<WindowInfosListenerInvoker>::make()) {}
+
+    ~WindowInfosListenerInvokerTest() {
+        std::mutex mutex;
+        std::condition_variable cv;
+        bool flushComplete = false;
+        // Flush the BackgroundExecutor thread to ensure any scheduled tasks are complete.
+        // Otherwise, references those tasks hold may go out of scope before they are done
+        // executing.
+        BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+            std::scoped_lock lock{mutex};
+            flushComplete = true;
+            cv.notify_one();
+        }});
+        std::unique_lock<std::mutex> lock{mutex};
+        cv.wait(lock, [&]() { return flushComplete; });
+    }
+
+    sp<WindowInfosListenerInvoker> mInvoker;
+};
+
+using WindowInfosUpdateConsumer = std::function<void(const gui::WindowInfosUpdate&,
+                                                     const sp<gui::IWindowInfosReportedListener>&)>;
+
+class Listener : public gui::BnWindowInfosListener {
+public:
+    Listener(WindowInfosUpdateConsumer consumer) : mConsumer(std::move(consumer)) {}
+
+    binder::Status onWindowInfosChanged(
+            const gui::WindowInfosUpdate& update,
+            const sp<gui::IWindowInfosReportedListener>& reportedListener) override {
+        mConsumer(update, reportedListener);
+        return binder::Status::ok();
+    }
+
+private:
+    WindowInfosUpdateConsumer mConsumer;
+};
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged calls a single window infos listener.
+TEST_F(WindowInfosListenerInvokerTest, callsSingleListener) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int callCount = 0;
+
+    mInvoker->addWindowInfosListener(
+            sp<Listener>::make([&](const gui::WindowInfosUpdate&,
+                                   const sp<gui::IWindowInfosReportedListener>& reportedListener) {
+                std::scoped_lock lock{mutex};
+                callCount++;
+                cv.notify_one();
+
+                reportedListener->onWindowInfosReported();
+            }));
+
+    BackgroundExecutor::getInstance().sendCallbacks(
+            {[this]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+    std::unique_lock<std::mutex> lock{mutex};
+    cv.wait(lock, [&]() { return callCount == 1; });
+    EXPECT_EQ(callCount, 1);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged calls multiple window infos listeners.
+TEST_F(WindowInfosListenerInvokerTest, callsMultipleListeners) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int callCount = 0;
+    const int expectedCallCount = 3;
+
+    for (int i = 0; i < expectedCallCount; i++) {
+        mInvoker->addWindowInfosListener(sp<Listener>::make(
+                [&](const gui::WindowInfosUpdate&,
+                    const sp<gui::IWindowInfosReportedListener>& reportedListener) {
+                    std::scoped_lock lock{mutex};
+                    callCount++;
+                    if (callCount == expectedCallCount) {
+                        cv.notify_one();
+                    }
+
+                    reportedListener->onWindowInfosReported();
+                }));
+    }
+
+    BackgroundExecutor::getInstance().sendCallbacks(
+            {[&]() { mInvoker->windowInfosChanged({}, {}, false); }});
+
+    std::unique_lock<std::mutex> lock{mutex};
+    cv.wait(lock, [&]() { return callCount == expectedCallCount; });
+    EXPECT_EQ(callCount, expectedCallCount);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged delays sending a second message until
+// after the WindowInfosReportedListener is called.
+TEST_F(WindowInfosListenerInvokerTest, delaysUnackedCall) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int callCount = 0;
+
+    // Simulate a slow ack by not calling the WindowInfosReportedListener.
+    mInvoker->addWindowInfosListener(sp<Listener>::make(
+            [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+                std::scoped_lock lock{mutex};
+                callCount++;
+                cv.notify_one();
+            }));
+
+    BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+        mInvoker->windowInfosChanged({}, {}, false);
+        mInvoker->windowInfosChanged({}, {}, false);
+    }});
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return callCount == 1; });
+    }
+    EXPECT_EQ(callCount, 1);
+
+    // Ack the first message.
+    mInvoker->onWindowInfosReported();
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return callCount == 2; });
+    }
+    EXPECT_EQ(callCount, 2);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged immediately sends a second message when
+// forceImmediateCall is true.
+TEST_F(WindowInfosListenerInvokerTest, sendsForcedMessage) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int callCount = 0;
+    const int expectedCallCount = 2;
+
+    // Simulate a slow ack by not calling the WindowInfosReportedListener.
+    mInvoker->addWindowInfosListener(sp<Listener>::make(
+            [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+                std::scoped_lock lock{mutex};
+                callCount++;
+                if (callCount == expectedCallCount) {
+                    cv.notify_one();
+                }
+            }));
+
+    BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+        mInvoker->windowInfosChanged({}, {}, false);
+        mInvoker->windowInfosChanged({}, {}, true);
+    }});
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return callCount == expectedCallCount; });
+    }
+    EXPECT_EQ(callCount, expectedCallCount);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged skips old messages when more than one
+// message is delayed.
+TEST_F(WindowInfosListenerInvokerTest, skipsDelayedMessage) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int64_t lastUpdateId = -1;
+
+    // Simulate a slow ack by not calling the WindowInfosReportedListener.
+    mInvoker->addWindowInfosListener(
+            sp<Listener>::make([&](const gui::WindowInfosUpdate& update,
+                                   const sp<gui::IWindowInfosReportedListener>&) {
+                std::scoped_lock lock{mutex};
+                lastUpdateId = update.vsyncId;
+                cv.notify_one();
+            }));
+
+    BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+        mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 1, 0}, {}, false);
+        mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 2, 0}, {}, false);
+        mInvoker->windowInfosChanged({{}, {}, /* vsyncId= */ 3, 0}, {}, false);
+    }});
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return lastUpdateId == 1; });
+    }
+    EXPECT_EQ(lastUpdateId, 1);
+
+    // Ack the first message. The third update should be sent.
+    mInvoker->onWindowInfosReported();
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return lastUpdateId == 3; });
+    }
+    EXPECT_EQ(lastUpdateId, 3);
+}
+
+// Test that WindowInfosListenerInvoker#windowInfosChanged immediately calls listener after a call
+// where no listeners were configured.
+TEST_F(WindowInfosListenerInvokerTest, noListeners) {
+    std::mutex mutex;
+    std::condition_variable cv;
+
+    int callCount = 0;
+
+    // Test that calling windowInfosChanged without any listeners doesn't cause the next call to be
+    // delayed.
+    BackgroundExecutor::getInstance().sendCallbacks({[&]() {
+        mInvoker->windowInfosChanged({}, {}, false);
+        mInvoker->addWindowInfosListener(sp<Listener>::make(
+                [&](const gui::WindowInfosUpdate&, const sp<gui::IWindowInfosReportedListener>&) {
+                    std::scoped_lock lock{mutex};
+                    callCount++;
+                    cv.notify_one();
+                }));
+        mInvoker->windowInfosChanged({}, {}, false);
+    }});
+
+    {
+        std::unique_lock lock{mutex};
+        cv.wait(lock, [&]() { return callCount == 1; });
+    }
+    EXPECT_EQ(callCount, 1);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
deleted file mode 100644
index 657ced3..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 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 <gmock/gmock.h>
-
-#include "DisplayHardware/PowerAdvisor.h"
-
-namespace android {
-namespace hardware {
-namespace power {
-class IPower;
-}
-} // namespace hardware
-} // namespace android
-
-namespace android::Hwc2::mock {
-
-class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper {
-public:
-    MockAidlPowerHalWrapper();
-    ~MockAidlPowerHalWrapper() override;
-    MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override));
-    MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override));
-    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
-    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, restartPowerHintSession, (), (override));
-    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
-                (override));
-    MOCK_METHOD(bool, startPowerHintSession, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override));
-    MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp),
-                (override));
-    MOCK_METHOD(bool, shouldReconnectHAL, (), (override));
-};
-
-} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index aa8b521..d3fb9fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
 using android::hardware::graphics::composer::V2_1::Config;
@@ -56,8 +55,7 @@
                  std::vector<aidl::android::hardware::graphics::composer3::Capability>());
     MOCK_METHOD0(dumpDebugInfo, std::string());
     MOCK_METHOD1(registerCallback, void(HWC2::ComposerCallback&));
-    MOCK_METHOD0(resetCommands, void());
-    MOCK_METHOD0(executeCommands, Error());
+    MOCK_METHOD1(executeCommands, Error(Display));
     MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t());
     MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*));
     MOCK_METHOD1(destroyVirtualDisplay, Error(Display));
@@ -99,6 +97,8 @@
                  Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
     MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
     MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
+    MOCK_METHOD4(setLayerBufferSlotsToClear,
+                 Error(Display, Layer, const std::vector<uint32_t>&, uint32_t));
     MOCK_METHOD3(setLayerSurfaceDamage,
                  Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
     MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
@@ -144,6 +144,12 @@
     MOCK_METHOD2(setBootDisplayConfig, Error(Display, Config));
     MOCK_METHOD1(clearBootDisplayConfig, Error(Display));
     MOCK_METHOD2(getPreferredBootDisplayConfig, Error(Display, Config*));
+    MOCK_METHOD1(getHdrConversionCapabilities,
+                 Error(std::vector<
+                         aidl::android::hardware::graphics::common::HdrConversionCapability>*));
+    MOCK_METHOD2(setHdrConversionStrategy,
+                 Error(aidl::android::hardware::graphics::common::HdrConversionStrategy,
+                       aidl::android::hardware::graphics::common::Hdr*));
     MOCK_METHOD2(getSupportedContentTypes,
                  V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
     MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
@@ -164,6 +170,11 @@
     MOCK_METHOD2(setIdleTimerEnabled, Error(Display, std::chrono::milliseconds));
     MOCK_METHOD2(hasDisplayIdleTimerCapability, Error(Display, bool*));
     MOCK_METHOD2(getPhysicalDisplayOrientation, Error(Display, AidlTransform*));
+    MOCK_METHOD1(getOverlaySupport,
+                 Error(aidl::android::hardware::graphics::composer3::OverlayProperties*));
+    MOCK_METHOD1(onHotplugConnect, void(Display));
+    MOCK_METHOD1(onHotplugDisconnect, void(Display));
+    MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index 6809580..3b36361 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -33,6 +33,11 @@
             .build();
 }
 
+inline DisplayModePtr createDisplayMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+                                        Fps refreshRate) {
+    return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
+}
+
 inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
     return DisplayMode::Builder(modePtr->getHwcId())
             .setId(modePtr->getId())
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 07cd15d..40f59b8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -105,6 +105,9 @@
     MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (), (const override));
     MOCK_METHOD(hal::Error, getPhysicalDisplayOrientation, (Hwc2::AidlTransform *),
                 (const override));
+    MOCK_METHOD(hal::Error, getOverlaySupport,
+                (aidl::android::hardware::graphics::composer3::OverlayProperties *),
+                (const override));
 };
 
 class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 439f6f4..f4ded21 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -23,6 +23,7 @@
 
 using android::binder::Status;
 using android::hardware::power::IPowerHintSession;
+using android::hardware::power::SessionHint;
 
 using namespace android::hardware::power;
 
@@ -40,6 +41,8 @@
     MOCK_METHOD(std::string, getInterfaceHash, (), (override));
     MOCK_METHOD(Status, updateTargetWorkDuration, (int64_t), (override));
     MOCK_METHOD(Status, reportActualWorkDuration, (const ::std::vector<WorkDuration>&), (override));
+    MOCK_METHOD(Status, sendHint, (SessionHint), (override));
+    MOCK_METHOD(Status, setThreads, (const ::std::vector<int32_t>&), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index aede250..3caa2b9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -32,37 +32,35 @@
     MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
                 (override));
     MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
-    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+    MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
     MOCK_METHOD(bool, usePowerHintSession, (), (override));
     MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
-    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
-    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override));
-    MOCK_METHOD(void, sendActualWorkDuration, (), (override));
-    MOCK_METHOD(void, sendPredictedWorkDuration, (), (override));
-    MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
+    MOCK_METHOD(bool, ensurePowerHintSessionRunning, (), (override));
+    MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
+    MOCK_METHOD(void, reportActualWorkDuration, (), (override));
+    MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
     MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override));
     MOCK_METHOD(void, setGpuFenceTime,
                 (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
     MOCK_METHOD(void, setHwcValidateTiming,
-                (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime),
+                (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentTiming,
-                (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime),
+                (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
     MOCK_METHOD(void, setRequiresClientComposition,
                 (DisplayId displayId, bool requiresClientComposition), (override));
-    MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override));
-    MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime),
+    MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
+    MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
                 (override));
     MOCK_METHOD(void, setHwcPresentDelayedTime,
-                (DisplayId displayId,
-                 std::chrono::steady_clock::time_point earliestFrameStartTime));
-    MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override));
-    MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override));
-    MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override));
+                (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, (int64_t targetDuration), (override));
+    MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override));
 };
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
similarity index 67%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
index 5049b1d..3ec5c2d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-#include "MockAidlPowerHalWrapper.h"
-#include "MockIPower.h"
+#include "MockPowerHalController.h"
 
 namespace android::Hwc2::mock {
 
-MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
-      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
-MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+MockPowerHalController::MockPowerHalController() = default;
+MockPowerHalController::~MockPowerHalController() = default;
 
 } // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
new file mode 100644
index 0000000..358395d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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 <scheduler/Time.h>
+
+#include <powermanager/PowerHalController.h>
+
+namespace android {
+namespace hardware {
+namespace power {
+class IPower;
+}
+} // namespace hardware
+} // namespace android
+
+namespace android::Hwc2::mock {
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::power::HalResult;
+
+class MockPowerHalController : public power::PowerHalController {
+public:
+    MockPowerHalController();
+    ~MockPowerHalController() override;
+    MOCK_METHOD(HalResult<void>, setBoost, (Boost, int32_t), (override));
+    MOCK_METHOD(HalResult<void>, setMode, (Mode, bool), (override));
+    MOCK_METHOD(HalResult<sp<hardware::power::IPowerHintSession>>, createHintSession,
+                (int32_t, int32_t, const std::vector<int32_t>&, int64_t), (override));
+    MOCK_METHOD(HalResult<int64_t>, getHintSessionPreferredRate, (), (override));
+};
+
+} // namespace android::Hwc2::mock
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
new file mode 100644
index 0000000..a71e82c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <android/gui/DisplayModeSpecs.h>
+
+namespace android::mock {
+
+inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching,
+                                                    float minFps, float maxFps) {
+    gui::DisplayModeSpecs specs;
+    specs.defaultMode = defaultMode;
+    specs.allowGroupSwitching = allowGroupSwitching;
+    specs.primaryRanges.physical.min = minFps;
+    specs.primaryRanges.physical.max = maxFps;
+    specs.primaryRanges.render = specs.primaryRanges.physical;
+    specs.appRequestRanges = specs.primaryRanges;
+    return specs;
+}
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index c5ca86a..8d57049 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -24,31 +24,33 @@
 
 class EventThread : public android::EventThread {
 public:
+    static constexpr auto kCallingUid = static_cast<uid_t>(0);
+
     EventThread();
     ~EventThread() override;
 
-    MOCK_CONST_METHOD2(createEventConnection,
-                       sp<EventThreadConnection>(ResyncCallback,
-                                                 ISurfaceComposer::EventRegistrationFlags));
-    MOCK_METHOD0(onScreenReleased, void());
-    MOCK_METHOD0(onScreenAcquired, void());
-    MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
-    MOCK_METHOD2(onFrameRateOverridesChanged,
-                 void(PhysicalDisplayId, std::vector<FrameRateOverride>));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-    MOCK_METHOD2(setDuration,
-                 void(std::chrono::nanoseconds workDuration,
-                      std::chrono::nanoseconds readyDuration));
-    MOCK_METHOD1(registerDisplayEventConnection,
-                 status_t(const sp<android::EventThreadConnection> &));
-    MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
-    MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+    MOCK_METHOD(sp<EventThreadConnection>, createEventConnection,
+                (ResyncCallback, EventRegistrationFlags), (const, override));
+    MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
+    MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
+    MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));
+    MOCK_METHOD(void, onFrameRateOverridesChanged,
+                (PhysicalDisplayId, std::vector<FrameRateOverride>), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
+    MOCK_METHOD(void, setDuration,
+                (std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration),
+                (override));
+    MOCK_METHOD(status_t, registerDisplayEventConnection,
+                (const sp<android::EventThreadConnection>&), (override));
+    MOCK_METHOD(void, setVsyncRate, (uint32_t, const sp<android::EventThreadConnection>&),
+                (override));
+    MOCK_METHOD(void, requestNextVsync, (const sp<android::EventThreadConnection>&), (override));
     MOCK_METHOD(VsyncEventData, getLatestVsyncEventData,
-                (const sp<android::EventThreadConnection> &), (const));
-    MOCK_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &));
-    MOCK_METHOD1(pauseVsyncCallback, void(bool));
-    MOCK_METHOD0(getEventThreadConnectionCount, size_t());
+                (const sp<android::EventThreadConnection>&), (const, override));
+    MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
+    MOCK_METHOD(void, pauseVsyncCallback, (bool));
+    MOCK_METHOD(size_t, getEventThreadConnectionCount, (), (override));
+    MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index 5049b1d..ef9cd9b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-#include "MockAidlPowerHalWrapper.h"
-#include "MockIPower.h"
+#pragma once
 
-namespace android::Hwc2::mock {
+#include <scheduler/FrameRateMode.h>
 
-MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
-      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
-MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
-
-} // namespace android::Hwc2::mock
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
+    EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 0840a2f..50e07fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -18,21 +18,24 @@
 
 #include <gmock/gmock.h>
 
-#include "Layer.h"
-
 namespace android::mock {
 
 class MockLayer : public Layer {
 public:
     MockLayer(SurfaceFlinger* flinger, std::string name)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
+        EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
+                .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default));
+    }
     explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
 
     MOCK_CONST_METHOD0(getType, const char*());
     MOCK_METHOD0(getFrameSelectionPriority, int32_t());
     MOCK_CONST_METHOD0(isVisible, bool());
-    MOCK_METHOD0(createClone, sp<Layer>());
+    MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
     MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+    MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
+                       scheduler::LayerInfo::FrameRateCompatibility());
     MOCK_CONST_METHOD0(getOwnerUid, uid_t());
     MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 5267586..a8eca21 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -18,20 +18,20 @@
 
 #include <gmock/gmock.h>
 
-#include "Scheduler/Scheduler.h"
+#include "Scheduler/ISchedulerCallback.h"
 
 namespace android::scheduler::mock {
 
 struct SchedulerCallback final : ISchedulerCallback {
-    MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
-    MOCK_METHOD(void, requestDisplayMode, (DisplayModePtr, DisplayModeEvent), (override));
+    MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, bool), (override));
+    MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
     MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
     MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
-    void setVsyncEnabled(bool) override {}
-    void requestDisplayMode(DisplayModePtr, DisplayModeEvent) override {}
+    void setVsyncEnabled(PhysicalDisplayId, bool) override {}
+    void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
     void kernelTimerChanged(bool) override {}
     void triggerOnFrameRateOverridesChanged() override {}
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
deleted file mode 100644
index 0a0e7b5..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include "mock/MockSurfaceInterceptor.h"
-
-namespace android::mock {
-
-// Explicit default instantiation is recommended.
-SurfaceInterceptor::SurfaceInterceptor() = default;
-SurfaceInterceptor::~SurfaceInterceptor() = default;
-
-} // namespace android::mock
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
deleted file mode 100644
index b085027..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 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 <gmock/gmock.h>
-
-#include "SurfaceInterceptor.h"
-
-namespace android::mock {
-
-class SurfaceInterceptor : public android::SurfaceInterceptor {
-public:
-    SurfaceInterceptor();
-    ~SurfaceInterceptor() override;
-
-    MOCK_METHOD2(enable,
-                 void(const SortedVector<sp<Layer>>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&));
-    MOCK_METHOD0(disable, void());
-    MOCK_METHOD0(isEnabled, bool());
-    MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&));
-    MOCK_METHOD1(binderDied, void(const wp<IBinder>&));
-    MOCK_METHOD7(saveTransaction,
-                 void(const Vector<ComposerState>&,
-                      const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&,
-                      const Vector<DisplayState>&, uint32_t, int, int, uint64_t));
-    MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
-    MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
-    MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
-    MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
-    MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
-    MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
-    MOCK_METHOD1(saveVSyncEvent, void(nsecs_t));
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
     TimeStats();
     ~TimeStats() override;
 
-    MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+    MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
     MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
     MOCK_METHOD0(isEnabled, bool());
     MOCK_METHOD0(miniDump, std::string());
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp
similarity index 60%
copy from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp
index 5049b1d..2817514 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#include "MockAidlPowerHalWrapper.h"
-#include "MockIPower.h"
+#include "mock/MockVSyncDispatch.h"
 
-namespace android::Hwc2::mock {
+namespace android::mock {
 
-MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
-      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
-MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;
+// Explicit default instantiation is recommended.
+VSyncDispatch::VSyncDispatch() = default;
+VSyncDispatch::~VSyncDispatch() = default;
 
-} // namespace android::Hwc2::mock
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
new file mode 100644
index 0000000..dc32ff9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 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 "Scheduler/VSyncDispatch.h"
+
+namespace android::mock {
+
+class VSyncDispatch : public android::scheduler::VSyncDispatch {
+public:
+    VSyncDispatch();
+    ~VSyncDispatch() override;
+
+    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
+    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
+    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 5b0c1f3..dcf25e1 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,6 +34,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setRenderRate, (Fps), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 4ef91da..69ec60a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -28,12 +28,12 @@
     ~VsyncController() override;
 
     MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
-    MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
-    MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
-    MOCK_METHOD1(setIgnorePresentFences, void(bool));
+    MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override));
+    MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override));
+    MOCK_METHOD(void, setIgnorePresentFences, (bool), (override));
     MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
 
-    MOCK_CONST_METHOD1(dump, void(std::string&));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
 };
 
 } // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
index 07916b6..38c422a 100644
--- a/services/surfaceflinger/tests/utils/ColorUtils.h
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -33,6 +33,10 @@
     static const Color WHITE;
     static const Color BLACK;
     static const Color TRANSPARENT;
+
+    half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; }
+
+    half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; }
 };
 
 const Color Color::RED{255, 0, 0, 255};
@@ -81,6 +85,14 @@
         }
         color = ret;
     }
+
+    static half3 toHalf3(const Color& color) {
+        return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f};
+    }
+
+    static half4 toHalf4(const Color& color) {
+        return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f};
+    }
 };
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index b75cf08..1675584 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,8 +15,10 @@
  */
 #pragma once
 
+#include <gui/AidlStatusUtil.h>
 #include <gui/SyncScreenCaptureListener.h>
 #include <private/gui/ComposerServiceAIDL.h>
+#include <ui/FenceResult.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
 #include <functional>
@@ -24,6 +26,8 @@
 
 namespace android {
 
+using gui::aidl_utils::statusTFromBinderStatus;
+
 namespace {
 
 // A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
@@ -36,18 +40,22 @@
         SurfaceComposerClient::Transaction().apply(true);
 
         captureArgs.dataspace = ui::Dataspace::V0_SRGB;
-        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
         binder::Status status = sf->captureDisplay(captureArgs, captureListener);
-
-        if (status.transactionError() != NO_ERROR) {
-            return status.transactionError();
+        status_t err = statusTFromBinderStatus(status);
+        if (err != NO_ERROR) {
+            return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
-        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+        // TODO(b/248317436): extend to cover all displays for multi-display devices
+        const auto display =
+                ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+        captureScreen(sc, display);
     }
 
     static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
@@ -70,13 +78,14 @@
         SurfaceComposerClient::Transaction().apply(true);
 
         captureArgs.dataspace = ui::Dataspace::V0_SRGB;
-        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
+        const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
         binder::Status status = sf->captureLayers(captureArgs, captureListener);
-        if (status.transactionError() != NO_ERROR) {
-            return status.transactionError();
+        status_t err = statusTFromBinderStatus(status);
+        if (err != NO_ERROR) {
+            return err;
         }
         captureResults = captureListener->waitForResults();
-        return captureResults.result;
+        return fenceStatus(captureResults.fenceResult);
     }
 
     static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
diff --git a/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
new file mode 100644
index 0000000..8e28a75
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/WindowInfosListenerUtils.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2023 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 <gui/SurfaceComposerClient.h>
+#include <private/android_filesystem_config.h>
+#include <cstdint>
+#include <future>
+
+namespace android {
+using Transaction = SurfaceComposerClient::Transaction;
+using gui::DisplayInfo;
+using gui::WindowInfo;
+
+using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>;
+
+class WindowInfosListenerUtils {
+public:
+    WindowInfosListenerUtils() { mClient = sp<SurfaceComposerClient>::make(); }
+
+    bool waitForWindowInfosPredicate(const WindowInfosPredicate& predicate) {
+        std::promise<void> promise;
+        auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise);
+        mClient->addWindowInfosListener(listener);
+        auto future = promise.get_future();
+        bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready;
+        mClient->removeWindowInfosListener(listener);
+        return satisfied;
+    }
+
+    static const WindowInfo* findMatchingWindowInfo(const WindowInfo& targetWindowInfo,
+                                                    const std::vector<WindowInfo>& windowInfos) {
+        for (const WindowInfo& windowInfo : windowInfos) {
+            if (windowInfo.token == targetWindowInfo.token) {
+                return &windowInfo;
+            }
+        }
+        return nullptr;
+    }
+
+private:
+    struct WindowInfosListener : public gui::WindowInfosListener {
+    public:
+        WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise)
+              : mPredicate(std::move(predicate)), mPromise(promise) {}
+
+        void onWindowInfosChanged(const gui::WindowInfosUpdate& update) override {
+            if (mPredicate(update.windowInfos)) {
+                mPromise.set_value();
+            }
+        }
+
+    private:
+        WindowInfosPredicate mPredicate;
+        std::promise<void>& mPromise;
+    };
+
+    sp<SurfaceComposerClient> mClient;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
index 667dfb9..8b4a6be 100644
--- a/services/surfaceflinger/tests/vsync/vsync.cpp
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -18,7 +18,13 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
+// This file is included by modules that have host support but android/looper.h is not supported
+// on host. __REMOVED_IN needs to be defined in order for android/looper.h to be compiled.
+#ifndef __BIONIC__
+#define __REMOVED_IN(x) __attribute__((deprecated))
+#endif
 #include <android/looper.h>
+
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Looper.h>
 
@@ -55,8 +61,7 @@
 {
     DisplayEventReceiver myDisplayEvent;
 
-
-    sp<Looper> loop = new Looper(false);
+    sp<Looper> loop = sp<Looper>::make(false);
     loop->addFd(myDisplayEvent.getFd(), 0, ALOOPER_EVENT_INPUT, receiver,
             &myDisplayEvent);