Merge "flatland: ConsumerBase-based classes now create their own BufferQueues" into main
diff --git a/Android.bp b/Android.bp
index 4befb1b..72311f0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -38,6 +38,7 @@
 
 cc_library_headers {
     name: "native_headers",
+    vendor_available: true,
     host_supported: true,
     target: {
         windows: {
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index ff9c4b0..5980d5d 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -44,7 +44,7 @@
      * samples by the end of the resampling. No other field of motionEvent should be modified.
      * - If resampling does not occur, then motionEvent must not be modified in any way.
      */
-    virtual void resampleMotionEvent(const std::chrono::nanoseconds resampleTime,
+    virtual void resampleMotionEvent(std::chrono::nanoseconds resampleTime,
                                      MotionEvent& motionEvent,
                                      const InputMessage* futureSample) = 0;
 };
@@ -60,7 +60,7 @@
      * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
      * `motionEvent` is unmodified.
      */
-    void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+    void resampleMotionEvent(std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
                              const InputMessage* futureSample) override;
 
 private:
@@ -72,10 +72,6 @@
     struct Sample {
         std::chrono::nanoseconds eventTime;
         Pointer pointer;
-
-        Sample(const std::chrono::nanoseconds eventTime, const PointerProperties& properties,
-               const PointerCoords& coords)
-              : eventTime{eventTime}, pointer{properties, coords} {}
     };
 
     /**
@@ -99,17 +95,34 @@
     void updateLatestSamples(const MotionEvent& motionEvent);
 
     /**
-     * May add a sample at the end of motionEvent with eventTime equal to resampleTime, and
-     * interpolated coordinates between the latest motionEvent sample and futureSample.
+     * Checks if there are necessary conditions to interpolate. For example, interpolation cannot
+     * take place if samples are too far apart in time. mLatestSamples must have at least one sample
+     * when canInterpolate is invoked.
      */
-    void interpolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
-                     const InputMessage& futureSample) const;
+    bool canInterpolate(const InputMessage& futureSample) const;
 
     /**
-     * May add a sample at the end of motionEvent by extrapolating from the latest two samples. The
-     * added sample either has eventTime equal to resampleTime, or an earlier time if resampleTime
-     * is too far in the future.
+     * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+     * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
+     * mLatestSamples must have at least one sample when attemptInterpolation is called.
      */
-    void extrapolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent) const;
+    std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
+                                               const InputMessage& futureSample) const;
+
+    /**
+     * Checks if there are necessary conditions to extrapolate. That is, there are at least two
+     * samples in mLatestSamples, and delta is bounded within a time interval.
+     */
+    bool canExtrapolate() const;
+
+    /**
+     * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from
+     * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime,
+     * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false,
+     * this function returns nullopt.
+     */
+    std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+
+    inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
 };
 } // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 26c228d..4e02ace 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -255,6 +255,9 @@
         "include_cpp/android/*.h",
     ],
     license: "NOTICE",
+    // These are intentionally not C. It's a mistake that they're in the NDK.
+    // See the bug above.
+    skip_verification: true,
 }
 
 ndk_library {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index cd78e82..3038de9 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1384,8 +1384,8 @@
     sp<IServiceManager> sm = defaultServiceManager();
     ASSERT_NE(nullptr, sm);
     // Any Java service with non-empty getInterfaceDescriptor() would do.
-    // Let's pick batteryproperties.
-    auto binder = sm->checkService(String16("batteryproperties"));
+    // Let's pick activity.
+    auto binder = sm->checkService(String16("activity"));
     ASSERT_NE(nullptr, binder);
     auto descriptor = binder->getInterfaceDescriptor();
     ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 3d1be4d..462081b 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -116,7 +116,7 @@
     // Public for testing.
     static nsecs_t snapToNextTick(
             nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
-    nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; };
+    nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; }
 
     nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
     nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 8fca946..3aac457 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -867,6 +867,6 @@
 #endif
 
 // ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 39207f8..0f51f2d 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -194,6 +194,14 @@
      * in <system/window.h>. */
     int setScalingMode(int mode);
 
+    virtual int setBuffersTimestamp(int64_t timestamp);
+    virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
+    virtual int setCrop(Rect const* rect);
+    virtual int setBuffersTransform(uint32_t transform);
+    virtual int setBuffersStickyTransform(uint32_t transform);
+    virtual int setBuffersFormat(PixelFormat format);
+    virtual int setUsage(uint64_t reqUsage);
+
     // See IGraphicBufferProducer::setDequeueTimeout
     status_t setDequeueTimeout(nsecs_t timeout);
 
@@ -354,16 +362,9 @@
     virtual int connect(int api);
     virtual int setBufferCount(int bufferCount);
     virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
-    virtual int setBuffersFormat(PixelFormat format);
-    virtual int setBuffersTransform(uint32_t transform);
-    virtual int setBuffersStickyTransform(uint32_t transform);
-    virtual int setBuffersTimestamp(int64_t timestamp);
-    virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
     virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
     virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
     virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
-    virtual int setCrop(Rect const* rect);
-    virtual int setUsage(uint64_t reqUsage);
     virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
 
 public:
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index b7aba2b..7ddac81 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -59,8 +59,9 @@
     // of the full parceling to happen on its native side.
     status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
 
-  private:
+    std::string toString() const;
 
+private:
     static String16 readMaybeEmptyString16(const Parcel* parcel);
 };
 
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 1b216e9..f07747f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -12,6 +12,34 @@
     default_applicable_licenses: ["frameworks_native_license"],
 }
 
+aidl_interface {
+    name: "libgui_test_server_aidl",
+    unstable: true,
+    srcs: ["testserver/aidl/**/*.aidl"],
+    local_include_dir: "testserver/aidl",
+    include_dirs: [
+        "frameworks/native/aidl/gui",
+    ],
+    backend: {
+        cpp: {
+            enabled: true,
+            additional_shared_libraries: [
+                "libgui",
+                "libui",
+            ],
+        },
+        java: {
+            enabled: false,
+        },
+        ndk: {
+            enabled: false,
+        },
+        rust: {
+            enabled: false,
+        },
+    },
+}
+
 cc_test {
     name: "libgui_test",
     test_suites: ["device-tests"],
@@ -30,7 +58,6 @@
     ],
 
     srcs: [
-        "LibGuiMain.cpp", // Custom gtest entrypoint
         "BLASTBufferQueue_test.cpp",
         "BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
@@ -38,24 +65,29 @@
         "Choreographer_test.cpp",
         "CompositorTiming_test.cpp",
         "CpuConsumer_test.cpp",
-        "EndToEndNativeInputTest.cpp",
-        "FrameRateUtilsTest.cpp",
-        "DisplayInfo_test.cpp",
         "DisplayedContentSampling_test.cpp",
+        "DisplayInfo_test.cpp",
+        "EndToEndNativeInputTest.cpp",
         "FillBuffer.cpp",
+        "FrameRateUtilsTest.cpp",
         "GLTest.cpp",
         "IGraphicBufferProducer_test.cpp",
+        "LibGuiMain.cpp", // Custom gtest entrypoint
         "Malicious.cpp",
         "MultiTextureConsumer_test.cpp",
         "RegionSampling_test.cpp",
         "StreamSplitter_test.cpp",
+        "Surface_test.cpp",
         "SurfaceTextureClient_test.cpp",
         "SurfaceTextureFBO_test.cpp",
+        "SurfaceTextureGL_test.cpp",
         "SurfaceTextureGLThreadToGL_test.cpp",
         "SurfaceTextureGLToGL_test.cpp",
-        "SurfaceTextureGL_test.cpp",
         "SurfaceTextureMultiContextGL_test.cpp",
-        "Surface_test.cpp",
+        "TestServer_test.cpp",
+        "testserver/TestServer.cpp",
+        "testserver/TestServerClient.cpp",
+        "testserver/TestServerHost.cpp",
         "TextureRenderer.cpp",
         "VsyncEventData_test.cpp",
         "WindowInfo_test.cpp",
@@ -66,10 +98,17 @@
         "android.hardware.configstore-utils",
         "libSurfaceFlingerProp",
         "libGLESv1_CM",
+        "libgui_test_server_aidl-cpp",
         "libinput",
         "libnativedisplay",
     ],
 
+    // This needs to get copied over for the test since it's not part of the
+    // platform.
+    data_libs: [
+        "libgui_test_server_aidl-cpp",
+    ],
+
     static_libs: [
         "libgmock",
     ],
diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp
index 10f7207..7c7c2cc 100644
--- a/libs/gui/tests/LibGuiMain.cpp
+++ b/libs/gui/tests/LibGuiMain.cpp
@@ -14,8 +14,15 @@
  * limitations under the License.
  */
 
-#include "gtest/gtest.h"
-#include "log/log.h"
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "testserver/TestServer.h"
+#include "testserver/TestServerClient.h"
+#include "testserver/TestServerHost.h"
+
+using namespace android;
 
 namespace {
 
@@ -32,7 +39,34 @@
 } // namespace
 
 int main(int argc, char** argv) {
+    // There are three modes that we can run in to support the libgui TestServer:
+    //
+    // - libgui_test : normal mode, runs tests and fork/execs the testserver host process
+    // - libgui_test --test-server-host $recvPipeFd $sendPipeFd : TestServerHost mode, listens on
+    //   $recvPipeFd for commands and sends responses over $sendPipeFd
+    // - libgui_test --test-server $name : TestServer mode, starts a ITestService binder service
+    //   under $name
+    for (int i = 1; i < argc; i++) {
+        std::string arg = argv[i];
+        if (arg == "--test-server-host") {
+            LOG_ALWAYS_FATAL_IF(argc < (i + 2), "--test-server-host requires two pipe fds");
+            // Note that the send/recv are from our perspective.
+            base::unique_fd recvPipeFd = base::unique_fd(atoi(argv[i + 1]));
+            base::unique_fd sendPipeFd = base::unique_fd(atoi(argv[i + 2]));
+            return TestServerHostMain(argv[0], std::move(sendPipeFd), std::move(recvPipeFd));
+        }
+        if (arg == "--test-server") {
+            LOG_ALWAYS_FATAL_IF(argc < (i + 1), "--test-server requires a name");
+            return TestServerMain(argv[i + 1]);
+        }
+    }
     testing::InitGoogleTest(&argc, argv);
     testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());
+
+    // This has to be run *before* any test initialization, because it fork/execs a TestServerHost,
+    // which will later create new binder service. You can't do that in a forked thread after you've
+    // initialized any binder stuff, which some tests do.
+    TestServerClient::InitializeOrDie(argv[0]);
+
     return RUN_ALL_TESTS();
 }
\ No newline at end of file
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8ab8783..4232443 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "gui/view/Surface.h"
 #include "Constants.h"
 #include "MockConsumer.h"
 
@@ -53,6 +54,8 @@
 #include <limits>
 #include <thread>
 
+#include "testserver/TestServerClient.h"
+
 namespace android {
 
 using namespace std::chrono_literals;
@@ -2363,6 +2366,14 @@
 
     EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU));
 }
+
+TEST_F(SurfaceTest, ViewSurface_toString) {
+    view::Surface surface{};
+    EXPECT_EQ("", surface.toString());
+
+    surface.name = String16("name");
+    EXPECT_EQ("name", surface.toString());
+}
 #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
 
 } // namespace android
diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp
new file mode 100644
index 0000000..8712988
--- /dev/null
+++ b/libs/gui/tests/TestServer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware_buffer.h>
+#include <binder/ProcessState.h>
+#include <com_android_graphics_libgui_flags.h>
+#include <configstore/Utils.h>
+#include <gui/AidlStatusUtil.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+#include <sys/types.h>
+#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+#include <ui/DisplayMode.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <cstddef>
+#include <limits>
+#include <thread>
+
+#include "binder/IInterface.h"
+#include "testserver/TestServerClient.h"
+
+namespace android {
+
+namespace {
+
+class TestServerTest : public ::testing::Test {
+protected:
+    TestServerTest() { ProcessState::self()->startThreadPool(); }
+};
+
+} // namespace
+
+TEST_F(TestServerTest, Create) {
+    EXPECT_NE(nullptr, TestServerClient::Create());
+}
+
+TEST_F(TestServerTest, CreateProducer) {
+    sp<TestServerClient> client = TestServerClient::Create();
+    EXPECT_NE(nullptr, client->CreateProducer());
+}
+
+TEST_F(TestServerTest, KillServer) {
+    class DeathWaiter : public IBinder::DeathRecipient {
+    public:
+        virtual void binderDied(const wp<IBinder>&) override { mPromise.set_value(true); }
+        std::future<bool> getFuture() { return mPromise.get_future(); }
+
+        std::promise<bool> mPromise;
+    };
+
+    sp<TestServerClient> client = TestServerClient::Create();
+    sp<IGraphicBufferProducer> producer = client->CreateProducer();
+    EXPECT_NE(nullptr, producer);
+
+    sp<DeathWaiter> deathWaiter = sp<DeathWaiter>::make();
+    EXPECT_EQ(OK, IInterface::asBinder(producer)->linkToDeath(deathWaiter));
+
+    auto deathWaiterFuture = deathWaiter->getFuture();
+    EXPECT_EQ(OK, client->Kill());
+    EXPECT_EQ(nullptr, client->CreateProducer());
+
+    EXPECT_TRUE(deathWaiterFuture.get());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp
new file mode 100644
index 0000000..cd8824e
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestServer"
+
+#include <android-base/stringprintf.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/view/Surface.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "TestServer.h"
+
+namespace android {
+
+namespace {
+class TestConsumerListener : public BnConsumerListener {
+    virtual void onFrameAvailable(const BufferItem&) override {}
+    virtual void onBuffersReleased() override {}
+    virtual void onSidebandStreamChanged() override {}
+};
+
+class TestServiceImpl : public libgui_test_server::BnTestServer {
+public:
+    TestServiceImpl(const char* name) : mName(name) {}
+
+    virtual binder::Status createProducer(view::Surface* out) override {
+        std::lock_guard<std::mutex> lock(mMutex);
+
+        BufferQueueHolder bq;
+        BufferQueue::createBufferQueue(&bq.producer, &bq.consumer);
+        sp<TestConsumerListener> listener = sp<TestConsumerListener>::make();
+        bq.consumer->consumerConnect(listener, /*controlledByApp*/ true);
+
+        uint64_t id = 0;
+        bq.producer->getUniqueId(&id);
+        std::string name = base::StringPrintf("%s-%" PRIu64, mName, id);
+
+        out->name = String16(name.c_str());
+        out->graphicBufferProducer = bq.producer;
+        mBqs.push_back(std::move(bq));
+
+        return binder::Status::ok();
+    }
+
+    virtual binder::Status killNow() override {
+        ALOGE("LibGUI Test Service %s dying in response to killNow", mName);
+        _exit(0);
+        // Not reached:
+        return binder::Status::ok();
+    }
+
+private:
+    std::mutex mMutex;
+    const char* mName;
+
+    struct BufferQueueHolder {
+        sp<IGraphicBufferProducer> producer;
+        sp<IGraphicBufferConsumer> consumer;
+    };
+
+    std::vector<BufferQueueHolder> mBqs;
+};
+} // namespace
+
+int TestServerMain(const char* name) {
+    ProcessState::self()->startThreadPool();
+
+    sp<TestServiceImpl> testService = sp<TestServiceImpl>::make(name);
+    ALOGE("service");
+    sp<IServiceManager> serviceManager(defaultServiceManager());
+    LOG_ALWAYS_FATAL_IF(OK != serviceManager->addService(String16(name), testService));
+
+    ALOGD("LibGUI Test Service %s STARTED", name);
+
+    IPCThreadState::self()->joinThreadPool();
+
+    ALOGW("LibGUI Test Service %s DIED", name);
+
+    return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServer.h b/libs/gui/tests/testserver/TestServer.h
new file mode 100644
index 0000000..4226f1b
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+/*
+ * Main method for a libgui ITestServer server.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param name The service name of the test server to start.
+ * @return retcode
+ */
+int TestServerMain(const char* name);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.cpp b/libs/gui/tests/testserver/TestServerClient.cpp
new file mode 100644
index 0000000..e388074
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/wait.h>
+#include <cerrno>
+#define LOG_TAG "TestServerClient"
+
+#include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <libgui_test_server/ITestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <csignal>
+#include <cstdlib>
+#include <mutex>
+#include <string>
+
+#include "TestServerClient.h"
+#include "TestServerCommon.h"
+
+namespace android {
+
+namespace {
+
+std::string GetUniqueServiceName() {
+    static std::atomic<int> uniqueId = 1;
+
+    pid_t pid = getpid();
+    int id = uniqueId++;
+    return base::StringPrintf("Libgui-TestServer-%d-%d", pid, id);
+}
+
+struct RemoteTestServerHostHolder {
+    RemoteTestServerHostHolder(pid_t pid, int sendFd, int recvFd)
+          : mPid(pid), mSendFd(sendFd), mRecvFd(recvFd) {}
+    ~RemoteTestServerHostHolder() {
+        std::lock_guard lock(mMutex);
+
+        kill(mPid, SIGKILL);
+        close(mSendFd);
+        close(mRecvFd);
+    }
+
+    pid_t CreateTestServerOrDie(std::string name) {
+        std::lock_guard lock(mMutex);
+
+        CreateServerRequest request;
+        strlcpy(request.name, name.c_str(), sizeof(request.name) / sizeof(request.name[0]));
+
+        ssize_t bytes = write(mSendFd, &request, sizeof(request));
+        LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+
+        CreateServerResponse response;
+        bytes = read(mRecvFd, &response, sizeof(response));
+        LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+
+        return response.pid;
+    }
+
+private:
+    std::mutex mMutex;
+
+    pid_t mPid;
+    int mSendFd;
+    int mRecvFd;
+};
+
+std::unique_ptr<RemoteTestServerHostHolder> g_remoteTestServerHostHolder = nullptr;
+
+} // namespace
+
+void TestServerClient::InitializeOrDie(const char* filename) {
+    int sendPipeFds[2];
+    int ret = pipe(sendPipeFds);
+    LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess send pipe");
+
+    int recvPipeFds[2];
+    ret = pipe(recvPipeFds);
+    LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess recv pipe");
+
+    pid_t childPid = fork();
+    LOG_ALWAYS_FATAL_IF(childPid < 0, "Unable to fork child process");
+
+    if (childPid == 0) {
+        // We forked!
+        close(sendPipeFds[1]);
+        close(recvPipeFds[0]);
+
+        // We'll be reading from the parent's "send" and writing to the parent's "recv".
+        std::string sendPipe = std::to_string(sendPipeFds[0]);
+        std::string recvPipe = std::to_string(recvPipeFds[1]);
+        char* args[] = {
+                const_cast<char*>(filename),
+                const_cast<char*>("--test-server-host"),
+                const_cast<char*>(sendPipe.c_str()),
+                const_cast<char*>(recvPipe.c_str()),
+                nullptr,
+        };
+
+        ret = execv(filename, args);
+        ALOGE("Failed to exec libguiTestServer. ret=%d errno=%d (%s)", ret, errno, strerror(errno));
+        status_t status = -errno;
+        write(recvPipeFds[1], &status, sizeof(status));
+        _exit(EXIT_FAILURE);
+    }
+
+    close(sendPipeFds[0]);
+    close(recvPipeFds[1]);
+
+    // Check for an OK status that the host started. If so, we're good to go.
+    status_t status;
+    ret = read(recvPipeFds[0], &status, sizeof(status));
+    LOG_ALWAYS_FATAL_IF(ret != sizeof(status), "Unable to read from pipe: %d", ret);
+    LOG_ALWAYS_FATAL_IF(OK != status, "Pipe returned failed status: %d", status);
+
+    g_remoteTestServerHostHolder =
+            std::make_unique<RemoteTestServerHostHolder>(childPid, sendPipeFds[1], recvPipeFds[0]);
+}
+
+sp<TestServerClient> TestServerClient::Create() {
+    std::string serviceName = GetUniqueServiceName();
+
+    pid_t childPid = g_remoteTestServerHostHolder->CreateTestServerOrDie(serviceName);
+    ALOGD("Created child server %s with pid %d", serviceName.c_str(), childPid);
+
+    sp<libgui_test_server::ITestServer> server =
+            waitForService<libgui_test_server::ITestServer>(String16(serviceName.c_str()));
+    LOG_ALWAYS_FATAL_IF(server == nullptr);
+    ALOGD("Created connected to child server %s", serviceName.c_str());
+
+    return sp<TestServerClient>::make(server);
+}
+
+TestServerClient::TestServerClient(const sp<libgui_test_server::ITestServer>& server)
+      : mServer(server) {}
+
+TestServerClient::~TestServerClient() {
+    Kill();
+}
+
+sp<IGraphicBufferProducer> TestServerClient::CreateProducer() {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (!mIsAlive) {
+        return nullptr;
+    }
+
+    view::Surface surface;
+    binder::Status status = mServer->createProducer(&surface);
+
+    if (!status.isOk()) {
+        ALOGE("Failed to create remote producer. Error: %s", status.exceptionMessage().c_str());
+        return nullptr;
+    }
+
+    if (!surface.graphicBufferProducer) {
+        ALOGE("Remote producer returned no IGBP.");
+        return nullptr;
+    }
+
+    return surface.graphicBufferProducer;
+}
+
+status_t TestServerClient::Kill() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mIsAlive) {
+        return DEAD_OBJECT;
+    }
+
+    mServer->killNow();
+    mServer = nullptr;
+    mIsAlive = false;
+
+    return OK;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.h b/libs/gui/tests/testserver/TestServerClient.h
new file mode 100644
index 0000000..5329634
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <libgui_test_server/ITestServer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class TestServerClient : public RefBase {
+public:
+    static void InitializeOrDie(const char* filename);
+    static sp<TestServerClient> Create();
+
+    TestServerClient(const sp<libgui_test_server::ITestServer>& server);
+    virtual ~TestServerClient() override;
+
+    sp<IGraphicBufferProducer> CreateProducer();
+    status_t Kill();
+
+private:
+    std::mutex mMutex;
+
+    sp<libgui_test_server::ITestServer> mServer;
+    bool mIsAlive = true;
+};
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerCommon.h b/libs/gui/tests/testserver/TestServerCommon.h
new file mode 100644
index 0000000..7370f20
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerCommon.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fcntl.h>
+
+namespace android {
+
+/*
+ * Test -> TestServerHost Request to create a new ITestServer fork.
+ */
+struct CreateServerRequest {
+    /*
+     * Service name for new ITestServer.
+     */
+    char name[128];
+};
+
+/*
+ * TestServerHost -> Test Response for creating an ITestServer fork.
+ */
+struct CreateServerResponse {
+    /*
+     * pid of new ITestServer.
+     */
+    pid_t pid;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.cpp b/libs/gui/tests/testserver/TestServerHost.cpp
new file mode 100644
index 0000000..696c3b9
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestServerHost"
+
+#include <android-base/unique_fd.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <memory>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <cstddef>
+#include <cstdlib>
+
+#include "TestServerCommon.h"
+#include "TestServerHost.h"
+
+namespace android {
+
+namespace {
+
+pid_t ForkTestServer(const char* filename, char* name) {
+    pid_t childPid = fork();
+    LOG_ALWAYS_FATAL_IF(childPid == -1);
+
+    if (childPid != 0) {
+        return childPid;
+    }
+
+    // We forked!
+    const char* test_server_flag = "--test-server";
+    char* args[] = {
+            const_cast<char*>(filename),
+            const_cast<char*>(test_server_flag),
+            name,
+            nullptr,
+    };
+
+    int ret = execv(filename, args);
+    ALOGE("Failed to exec libgui_test as a TestServer. ret=%d errno=%d (%s)", ret, errno,
+          strerror(errno));
+    _exit(EXIT_FAILURE);
+}
+
+} // namespace
+
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+                       base::unique_fd recvPipeFd) {
+    status_t status = OK;
+    LOG_ALWAYS_FATAL_IF(sizeof(status) != write(sendPipeFd.get(), &status, sizeof(status)));
+
+    ALOGE("Launched TestServerHost");
+
+    while (true) {
+        CreateServerRequest request = {};
+        ssize_t bytes = read(recvPipeFd.get(), &request, sizeof(request));
+        LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+        pid_t childPid = ForkTestServer(filename, request.name);
+
+        CreateServerResponse response = {};
+        response.pid = childPid;
+        bytes = write(sendPipeFd.get(), &response, sizeof(response));
+        LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+    }
+
+    return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.h b/libs/gui/tests/testserver/TestServerHost.h
new file mode 100644
index 0000000..df22c0c
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <string>
+
+namespace android {
+
+/*
+ * Main method for a host process for TestServers.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param filename File name of this binary / the binary to execve into
+ * @param sendPipeFd Pipe FD to send data to.
+ * @param recvPipeFd Pipe FD to receive data from.
+ * @return retcode
+ */
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+                       base::unique_fd recvPipeFd);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
new file mode 100644
index 0000000..c939ea0
--- /dev/null
+++ b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
@@ -0,0 +1,12 @@
+package libgui_test_server;
+
+import android.view.Surface;
+
+// Test server for libgui_test
+interface ITestServer {
+    // Create a new producer. The server will have connected to the consumer.
+    Surface createProducer();
+
+    // Kills the server immediately.
+    void killNow();
+}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 7c15e7c..84c2a6a 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,5 +121,11 @@
     return str.value_or(String16());
 }
 
+std::string Surface::toString() const {
+    std::stringstream out;
+    out << name;
+    return out.str();
+}
+
 } // namespace view
 } // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index af8354c..342f7f5 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -60,10 +60,10 @@
     return a + alpha * (b - a);
 }
 
-const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
-                                             const float alpha) {
-    // Ensure the struct PointerCoords is initialized.
-    PointerCoords resampledCoords{};
+PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
+                                       float alpha) {
+    // We use the value of alpha to initialize resampledCoords with the latest sample information.
+    PointerCoords resampledCoords = (alpha < 1.0f) ? a : b;
     resampledCoords.isResampled = true;
     resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
     resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
@@ -72,52 +72,85 @@
 } // namespace
 
 void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
-    const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1;
-    for (size_t i = 0; i < motionEventSampleSize; ++i) {
-        Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
-                      *motionEvent.getPointerProperties(0),
-                      motionEvent.getSamplePointerCoords()[i]};
-        mLatestSamples.pushBack(sample);
+    const size_t numSamples = motionEvent.getHistorySize() + 1;
+    for (size_t i = 0; i < numSamples; ++i) {
+        mLatestSamples.pushBack(
+                Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
+                       Pointer{*motionEvent.getPointerProperties(0),
+                               motionEvent.getSamplePointerCoords()[i]}});
     }
 }
 
-void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent,
-                                  const InputMessage& futureSample) const {
-    const Sample pastSample = mLatestSamples.back();
+bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const {
+    LOG_IF(FATAL, mLatestSamples.empty())
+            << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+    const Sample& pastSample = *(mLatestSamples.end() - 1);
     const nanoseconds delta =
             static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
         LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
-        return;
+        return false;
     }
+    return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
+        nanoseconds resampleTime, const InputMessage& futureSample) const {
+    if (!canInterpolate(futureSample)) {
+        return std::nullopt;
+    }
+    LOG_IF(FATAL, mLatestSamples.empty())
+            << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+    const Sample& pastSample = *(mLatestSamples.end() - 1);
+    const nanoseconds delta =
+            static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
     const float alpha =
             std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
-
     const PointerCoords resampledCoords =
             calculateResampledCoords(pastSample.pointer.coords,
                                      futureSample.body.motion.pointers[0].coords, alpha);
-    motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId());
+
+    return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}};
 }
 
-void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const {
+bool LegacyResampler::canExtrapolate() const {
     if (mLatestSamples.size() < 2) {
-        return;
+        LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
+        return false;
     }
-    const Sample pastSample = *(mLatestSamples.end() - 2);
-    const Sample presentSample = *(mLatestSamples.end() - 1);
-    const nanoseconds delta =
-            static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime);
+
+    const Sample& pastSample = *(mLatestSamples.end() - 2);
+    const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+    const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
         LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
-        return;
+        return false;
     } else if (delta > RESAMPLE_MAX_DELTA) {
         LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
-        return;
+        return false;
     }
+    return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation(
+        nanoseconds resampleTime) const {
+    if (!canExtrapolate()) {
+        return std::nullopt;
+    }
+    LOG_IF(FATAL, mLatestSamples.size() < 2)
+            << "Not resampled. mLatestSamples must have at least two samples to extrapolate.";
+
+    const Sample& pastSample = *(mLatestSamples.end() - 2);
+    const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+    const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
     // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
     // we use this value as the resample time target.
-    const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) +
-            std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
+    const nanoseconds farthestPrediction =
+            presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
     const nanoseconds newResampleTime =
             (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
     LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
@@ -127,25 +160,32 @@
     const float alpha =
             std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
             delta;
-
     const PointerCoords resampledCoords =
             calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
                                      alpha);
-    motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId());
+
+    return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}};
 }
 
-void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent,
+inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
+                                                    MotionEvent& motionEvent) {
+    motionEvent.addSample(sample.eventTime.count(), &sample.pointer.coords, motionEvent.getId());
+}
+
+void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,
                                           const InputMessage* futureSample) {
     if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
         mLatestSamples.clear();
     }
     mPreviousDeviceId = motionEvent.getDeviceId();
+
     updateLatestSamples(motionEvent);
-    if (futureSample) {
-        interpolate(resampleTime, motionEvent, *futureSample);
-    } else {
-        extrapolate(resampleTime, motionEvent);
+
+    const std::optional<Sample> sample = (futureSample != nullptr)
+            ? (attemptInterpolation(resampleTime, *futureSample))
+            : (attemptExtrapolation(resampleTime));
+    if (sample.has_value()) {
+        addSampleToMotionEvent(*sample, motionEvent);
     }
-    LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
 }
 } // namespace android
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index e160ca0..135f8b4 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -229,6 +229,38 @@
     EXPECT_EQ(originalSampleSize, notResampledSampleSize);
 }
 
+TEST_F(ResamplerTest, NonResampledAxesArePreserved) {
+    constexpr float TOUCH_MAJOR_VALUE = 1.0f;
+
+    MotionEvent motionEvent =
+            InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+                        AMOTION_EVENT_ACTION_MOVE};
+
+    constexpr std::chrono::nanoseconds eventTime{10ms};
+    PointerCoords pointerCoords{};
+    pointerCoords.isResampled = false;
+    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 2.0f);
+    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2.0f);
+    pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, TOUCH_MAJOR_VALUE);
+
+    motionEvent.addSample(eventTime.count(), &pointerCoords, motionEvent.getId());
+
+    const InputMessage futureSample =
+            InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 4.0f, .isResampled = false}}};
+
+    const MotionEvent originalMotionEvent = motionEvent;
+
+    mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+    EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE);
+
+    assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+                                              Pointer{.id = 0,
+                                                      .x = 2.2f,
+                                                      .y = 2.4f,
+                                                      .isResampled = true});
+}
+
 TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
     MotionEvent motionEvent =
             InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
index 1dbcc29..4164c4b 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -19,6 +19,7 @@
 #include <SkRuntimeEffect.h>
 #include <SkStream.h>
 #include <SkString.h>
+#include <com_android_graphics_libgui_flags.h>
 #include "log/log_main.h"
 
 namespace android::renderengine::skia {
@@ -56,25 +57,33 @@
     }
 )");
 
+EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() {
+    if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+        return;
+    }
+    mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader));
+    LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(),
+                        "EdgeExtensionShaderFactory compilation "
+                        "failed with an unexpected error: %s",
+                        mResult->errorText.c_str());
+}
+
 sk_sp<SkShader> EdgeExtensionShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
                                                            const LayerSettings& layer,
-                                                           const SkRect& imageBounds) {
-    if (mBuilder == nullptr) {
-        const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(edgeShader);
-        if (!instance.errorText.isEmpty()) {
-            ALOGE("EdgeExtensionShaderFactory terminated with an error: %s",
-                  instance.errorText.c_str());
-            return nullptr;
-        }
-        mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
-    }
-    mBuilder->child("uContentTexture") = inputShader;
+                                                           const SkRect& imageBounds) const {
+    LOG_ALWAYS_FATAL_IF(mResult == nullptr,
+                        "EdgeExtensionShaderFactory did not initialize mResult. "
+                        "This means that we unexpectedly applied the edge extension shader");
+
+    SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(mResult->effect);
+
+    builder.child("uContentTexture") = inputShader;
     if (imageBounds.isEmpty()) {
-        mBuilder->uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
-                                                layer.geometry.boundaries.getHeight()};
+        builder.uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
+                                              layer.geometry.boundaries.getHeight()};
     } else {
-        mBuilder->uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
+        builder.uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
     }
-    return mBuilder->makeShader();
+    return builder.makeShader();
 }
 } // namespace android::renderengine::skia
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
index b0a8a93..17c6b91 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
@@ -33,10 +33,12 @@
  */
 class EdgeExtensionShaderFactory {
 public:
+    EdgeExtensionShaderFactory();
+
     sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, const LayerSettings& layer,
-                                   const SkRect& imageBounds);
+                                   const SkRect& imageBounds) const;
 
 private:
-    std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+    std::unique_ptr<const SkRuntimeEffect::Result> mResult;
 };
 } // namespace android::renderengine::skia
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 5673678..7b4a86c 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -84,7 +84,7 @@
             std::ostringstream oss;
             oss << sensor.getStringType() << ":" << sensor.getName();
             if (outString) {
-                *outString = std::move(oss.str());
+                *outString = oss.str();
             }
             return true;
         }
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 9aae145..a75ba37 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -156,6 +156,6 @@
     base::unique_fd mFenceFd;
 };
 
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_FENCE_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 652d8ba..936bf8f 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -297,6 +297,6 @@
             mDeathCallbacks;
 };
 
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_GRAPHIC_BUFFER_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index bbb2d77..97ed05a 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -137,6 +137,6 @@
 };
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_BUFFER_ALLOCATOR_H
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 9da1447..91aabe9 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -188,7 +188,7 @@
 
 // ---------------------------------------------------------------------------
 
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_UI_BUFFER_MAPPER_H
 
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index cf5c2e8..1f20787 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -72,6 +72,6 @@
 
 uint32_t bytesPerPixel(PixelFormat format);
 
-}; // namespace android
+} // namespace android
 
 #endif // UI_PIXELFORMAT_H
diff --git a/libs/ui/include/ui/Point.h b/libs/ui/include/ui/Point.h
index d050ede..97a54be 100644
--- a/libs/ui/include/ui/Point.h
+++ b/libs/ui/include/ui/Point.h
@@ -83,6 +83,6 @@
 
 ANDROID_BASIC_TYPES_TRAITS(Point)
 
-}; // namespace android
+} // namespace android
 
 #endif // ANDROID_UI_POINT
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 9e24a07..2eb9330 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -233,7 +233,7 @@
 
 ANDROID_BASIC_TYPES_TRAITS(Rect)
 
-}; // namespace android
+} // namespace android
 
 namespace std {
 template <>
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 927c334..d1b38f3 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -233,7 +233,7 @@
 }
 
 // ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
 
 namespace std {
 template <>
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 4454f36..37dc931 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -30,6 +30,10 @@
     to: "",
     srcs: ["include/EGL/**/*.h"],
     license: "include/EGL/NOTICE",
+    // eglext.h is not self-contained. Safe to skip C-compat verification
+    // though since upstream also cares about C compatibility, and the header is
+    // auto-generated anyway.
+    skip_verification: true,
 }
 
 ndk_headers {
@@ -38,6 +42,10 @@
     to: "",
     srcs: ["include/GLES/**/*.h"],
     license: "include/GLES/NOTICE",
+    // glext.h is not self-contained. Safe to skip C-compat verification
+    // though since upstream also cares about C compatibility, and the header is
+    // auto-generated anyway.
+    skip_verification: true,
 }
 
 ndk_headers {
@@ -46,6 +54,10 @@
     to: "",
     srcs: ["include/GLES2/**/*.h"],
     license: "include/GLES2/NOTICE",
+    // gl2ext.h is not self-contained. Safe to skip C-compat verification
+    // though since upstream also cares about C compatibility, and the header is
+    // auto-generated anyway.
+    skip_verification: true,
 }
 
 ndk_headers {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0384257..bcef350 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2713,7 +2713,8 @@
     {
         std::vector<TouchedWindow> hoveringWindows =
                 getHoveringWindowsLocked(oldState, tempTouchState, entry,
-                                         [this]() REQUIRES(mLock) { logDispatchStateLocked(); });
+                                         std::bind_front(&InputDispatcher::logDispatchStateLocked,
+                                                         this));
         // Hardcode to single hovering pointer for now.
         std::bitset<MAX_POINTER_ID + 1> pointerIds;
         pointerIds.set(entry.pointerProperties[0].id);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index ed4c47d..b4ac9ba 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -149,6 +149,7 @@
         "tests/CompositionEngineTest.cpp",
         "tests/DisplayColorProfileTest.cpp",
         "tests/DisplayTest.cpp",
+        "tests/HwcAsyncWorkerTest.cpp",
         "tests/HwcBufferCacheTest.cpp",
         "tests/MockHWC2.cpp",
         "tests/MockHWComposer.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
index 6086f0b..91385b4 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -24,6 +24,7 @@
 
 #include <android-base/thread_annotations.h>
 #include <cutils/sched_policy.h>
+#include <ftl/fake_guard.h>
 
 namespace android::compositionengine::impl {
 
@@ -60,7 +61,7 @@
     std::unique_lock<std::mutex> lock(mMutex);
     android::base::ScopedLockAssertion assumeLock(mMutex);
     while (!mDone) {
-        mCv.wait(lock);
+        mCv.wait(lock, [this]() FTL_FAKE_GUARD(mMutex) { return mTaskRequested || mDone; });
         if (mTaskRequested && mTask.valid()) {
             mTask();
             mTaskRequested = false;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 64cded8..2d8f98f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1101,10 +1101,10 @@
 }
 
 ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
-    return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
+    return ftl::Future<bool>(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
                presentFrameAndReleaseLayers(flushEvenWhenDisabled);
                return true;
-           })))
+           }))
             .then([](bool) { return std::monostate{}; });
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
new file mode 100644
index 0000000..dd04df6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <future>
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+using namespace std::chrono_literals;
+
+// For the edge case tests below, how much real time should be spent trying to reproduce edge cases
+// problems in a loop.
+//
+// Larger values mean problems are more likely to be detected, at the cost of making the unit test
+// run slower.
+//
+// As we expect the tests to be run continuously, even a short loop will eventually catch
+// problems, though not necessarily from changes in the same build that introduce them.
+constexpr auto kWallTimeForEdgeCaseTests = 5ms;
+
+TEST(HwcAsyncWorker, continuousTasksEdgeCase) {
+    // Ensures that a single worker that is given multiple tasks in short succession will run them.
+
+    impl::HwcAsyncWorker worker;
+    const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+    while (std::chrono::steady_clock::now() < endTime) {
+        auto f1 = worker.send([] { return false; });
+        EXPECT_FALSE(f1.get());
+        auto f2 = worker.send([] { return true; });
+        EXPECT_TRUE(f2.get());
+    }
+}
+
+TEST(HwcAsyncWorker, constructAndDestroyEdgeCase) {
+    // Ensures that newly created HwcAsyncWorkers can be immediately destroyed.
+
+    const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+    while (std::chrono::steady_clock::now() < endTime) {
+        impl::HwcAsyncWorker worker;
+    }
+}
+
+TEST(HwcAsyncWorker, newlyCreatedRunsTasksEdgeCase) {
+    // Ensures that newly created HwcAsyncWorkers will run a task if given one immediately.
+
+    const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+    while (std::chrono::steady_clock::now() < endTime) {
+        impl::HwcAsyncWorker worker;
+        auto f = worker.send([] { return true; });
+        f.get();
+    }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index c43d585..0e9218c 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -22,7 +22,9 @@
 #include "Display/DisplaySnapshot.h"
 #include "DisplayHardware/HWComposer.h"
 
+#include <android-base/properties.h>
 #include <common/FlagManager.h>
+#include <common/trace.h>
 #include <ftl/concat.h>
 #include <ftl/expected.h>
 #include <log/log.h>
@@ -237,4 +239,63 @@
     }
 }
 
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId) {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+    const auto controllerOpt = displayPtr->selectorPtr->kernelIdleTimerController();
+    if (!controllerOpt) return;
+
+    using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
+
+    switch (displayPtr->selectorPtr->getIdleTimerAction()) {
+        case KernelIdleTimerAction::TurnOff:
+            if (displayPtr->isKernelIdleTimerEnabled) {
+                SFTRACE_INT("KernelIdleTimer", 0);
+                updateKernelIdleTimer(displayId, std::chrono::milliseconds::zero(), *controllerOpt);
+                displayPtr->isKernelIdleTimerEnabled = false;
+            }
+            break;
+        case KernelIdleTimerAction::TurnOn:
+            if (!displayPtr->isKernelIdleTimerEnabled) {
+                SFTRACE_INT("KernelIdleTimer", 1);
+                const auto timeout = displayPtr->selectorPtr->getIdleTimerTimeout();
+                updateKernelIdleTimer(displayId, timeout, *controllerOpt);
+                displayPtr->isKernelIdleTimerEnabled = true;
+            }
+            break;
+    }
+}
+
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId,
+                                                  std::chrono::milliseconds timeout,
+                                                  KernelIdleTimerController controller) {
+    switch (controller) {
+        case KernelIdleTimerController::HwcApi:
+            mComposerPtr->setIdleTimerEnabled(displayId, timeout);
+            break;
+
+        case KernelIdleTimerController::Sysprop:
+            using namespace std::string_literals;
+            base::SetProperty("graphics.display.kernel_idle_timer.enabled"s,
+                              timeout > std::chrono::milliseconds::zero() ? "true"s : "false"s);
+            break;
+    }
+}
+
+auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const
+        -> KernelIdleTimerState {
+    std::lock_guard lock(mDisplayLock);
+    const auto& displayPtr =
+            FTL_EXPECT(mDisplays.get(displayId).ok_or(KernelIdleTimerState())).get();
+
+    const auto desiredModeIdOpt =
+            (std::scoped_lock(displayPtr->desiredModeLock), displayPtr->desiredModeOpt)
+                    .transform([](const display::DisplayModeRequest& request) {
+                        return request.mode.modePtr->getId();
+                    });
+
+    return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
+}
+
 } // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 258b04b..9ec603d 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -97,6 +97,17 @@
     void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
             EXCLUDES(mDisplayLock);
 
+    void updateKernelIdleTimer(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+            EXCLUDES(mDisplayLock);
+
+    struct KernelIdleTimerState {
+        std::optional<DisplayModeId> desiredModeIdOpt = std::nullopt;
+        bool isEnabled = false;
+    };
+
+    KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const
+            REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
 private:
     struct Display {
         template <size_t N>
@@ -121,6 +132,8 @@
 
         DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
         bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
+
+        bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false;
     };
 
     using DisplayPtr = std::unique_ptr<Display>;
@@ -128,6 +141,10 @@
     void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
             REQUIRES(mDisplayLock);
 
+    using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
+    void updateKernelIdleTimer(PhysicalDisplayId, std::chrono::milliseconds timeout,
+                               KernelIdleTimerController) REQUIRES(mDisplayLock);
+
     // Set once when initializing the DisplayModeController, which the HWComposer must outlive.
     HWComposer* mComposerPtr = nullptr;
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 75b07a8..402a3d2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,19 +201,6 @@
     return mPowerMode != hal::PowerMode::OFF;
 }
 
-nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
-    const auto physicalId = getPhysicalId();
-    if (!mHwComposer.isConnected(physicalId)) {
-        return 0;
-    }
-
-    if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) {
-        return *vsyncPeriodOpt;
-    }
-
-    return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs();
-}
-
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1b8a3a8..3e3f558 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -203,8 +203,6 @@
     void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
     bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
 
-    nsecs_t getVsyncPeriodFromHWC() const;
-
     Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
 
     // Round the requested refresh rate to match a divisor of the pacesetter
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index d50a0bc..0eced73 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,6 +44,7 @@
 using aidl::android::hardware::graphics::composer3::BnComposerCallback;
 using aidl::android::hardware::graphics::composer3::Capability;
 using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
+using aidl::android::hardware::graphics::composer3::Lut;
 using aidl::android::hardware::graphics::composer3::PowerMode;
 using aidl::android::hardware::graphics::composer3::VirtualDisplay;
 
@@ -1539,6 +1540,18 @@
     return error;
 }
 
+Error AidlComposer::getDisplayLuts(Display display, std::vector<Lut>* outLuts) {
+    Error error = Error::NONE;
+    mMutex.lock_shared();
+    if (auto reader = getReader(display)) {
+        *outLuts = reader->get().takeDisplayLuts(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();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ea0e53a..3669d4c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -244,6 +244,9 @@
     Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
     Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
                                 int32_t frameIntervalNs) override;
+    Error getDisplayLuts(
+            Display display,
+            std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override;
 
 private:
     // Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index bc067a0..888dc08 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -41,6 +41,7 @@
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
 #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 
 #include <aidl/android/hardware/graphics/common/Transform.h>
@@ -303,6 +304,7 @@
     virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
     virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
                                         int32_t frameIntervalNs) = 0;
+    virtual Error getDisplayLuts(Display display, std::vector<V3_0::Lut>* outLuts) = 0;
 };
 
 } // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8c0f81e..d5f65c6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -41,6 +41,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::Lut;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -607,6 +608,18 @@
     return static_cast<Error>(error);
 }
 
+Error Display::getDisplayLuts(std::vector<Lut>* outLuts) {
+    std::vector<Lut> tmpLuts;
+    const auto error = mComposer.getDisplayLuts(mId, &tmpLuts);
+    for (Lut& lut : tmpLuts) {
+        if (lut.pfd.get() >= 0) {
+            outLuts->push_back(
+                    {lut.layer, ndk::ScopedFileDescriptor(lut.pfd.release()), lut.lutProperties});
+        }
+    }
+    return static_cast<Error>(error);
+}
+
 Error Display::getDisplayDecorationSupport(
         std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                 support) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 5b94831..be2059a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -45,6 +45,7 @@
 #include <aidl/android/hardware/graphics/composer3/Color.h>
 #include <aidl/android/hardware/graphics/composer3/Composition.h>
 #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
 #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
 #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
 
@@ -178,6 +179,8 @@
     [[nodiscard]] virtual hal::Error getClientTargetProperty(
             aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
                     outClientTargetProperty) = 0;
+    [[nodiscard]] virtual hal::Error getDisplayLuts(
+            std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) = 0;
     [[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) = 0;
@@ -261,6 +264,8 @@
     hal::Error getClientTargetProperty(
             aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
                     outClientTargetProperty) override;
+    hal::Error getDisplayLuts(
+            std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override;
     hal::Error getDisplayDecorationSupport(
             std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
                     support) override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 7c4aa75..ec2a3ec 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -46,6 +46,7 @@
 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::Lut;
 using aidl::android::hardware::graphics::composer3::OverlayProperties;
 
 namespace android {
@@ -1408,6 +1409,10 @@
     return Error::NONE;
 }
 
+Error HidlComposer::getDisplayLuts(Display, std::vector<Lut>*) {
+    return Error::NONE;
+}
+
 Error HidlComposer::setLayerBrightness(Display, Layer, float) {
     return Error::NONE;
 }
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d78bfb7..8bca5ad 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -351,6 +351,8 @@
                                    Hdr*) override;
     Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
     Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
+    Error getDisplayLuts(Display,
+                         std::vector<aidl::android::hardware::graphics::composer3::Lut>*) override;
 
 private:
     class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 47fd700..c874db3 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -561,6 +561,7 @@
         updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
     }
 
+    bool childHasValidFrameRate = false;
     for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
         LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
                                                                 childHierarchy->getLayer()->id,
@@ -568,7 +569,8 @@
         const LayerSnapshot& childSnapshot =
                 updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
                                            depth + 1);
-        updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args);
+        updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(),
+                                         args, &childHasValidFrameRate);
     }
 
     return *snapshot;
@@ -675,9 +677,10 @@
     }
 }
 
-void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
-                                                            const LayerSnapshot& childSnapshot,
-                                                            const Args& args) {
+void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(
+        LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+        const RequestedLayerState& /* requestedChildState */, const Args& args,
+        bool* outChildHasValidFrameRate) {
     if (args.forceUpdate == ForceUpdateFlags::NONE &&
         !args.layerLifecycleManager.getGlobalChanges().any(
                 RequestedLayerState::Changes::Hierarchy) &&
@@ -687,7 +690,7 @@
     }
 
     using FrameRateCompatibility = scheduler::FrameRateCompatibility;
-    if (snapshot.frameRate.isValid()) {
+    if (snapshot.inheritedFrameRate.isValid() || *outChildHasValidFrameRate) {
         // we already have a valid framerate.
         return;
     }
@@ -704,13 +707,18 @@
     const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.vote.rate.isValid() &&
             childSnapshot.frameRate.vote.type == FrameRateCompatibility::Exact;
 
-    bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+    *outChildHasValidFrameRate |= layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
             layerVotedWithCategory || 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 (childHasValidFrameRate) {
-        snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+    static const auto noVote =
+            scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+    if (*outChildHasValidFrameRate) {
+        snapshot.frameRate = noVote;
+        snapshot.changes |= RequestedLayerState::Changes::FrameRate;
+    } else if (snapshot.frameRate != snapshot.inheritedFrameRate) {
+        snapshot.frameRate = snapshot.inheritedFrameRate;
         snapshot.changes |= RequestedLayerState::Changes::FrameRate;
     }
 }
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index f3c56a4..207e23a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -130,7 +130,9 @@
                                   const RequestedLayerState& layer,
                                   const LayerSnapshot& parentSnapshot);
     void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
-                                          const LayerSnapshot& childSnapshot, const Args& args);
+                                          const LayerSnapshot& childSnapshot,
+                                          const RequestedLayerState& requestedCHildState,
+                                          const Args& args, bool* outChildHasValidFrameRate);
     void updateTouchableRegionCrop(const Args& args);
 
     std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1258509..abdf92c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -316,7 +316,7 @@
 void Layer::addToCurrentState() {
     if (mRemovedFromDrawingState) {
         mRemovedFromDrawingState = false;
-        mFlinger->mScheduler->registerLayer(this);
+        mFlinger->mScheduler->registerLayer(this, FrameRateCompatibility::Default);
         mFlinger->removeFromOffscreenLayers(this);
     }
 
@@ -1100,42 +1100,6 @@
     return true;
 }
 
-bool Layer::setFrameRateSelectionPriority(int32_t priority) {
-    if (mDrawingState.frameRateSelectionPriority == priority) return false;
-    mDrawingState.frameRateSelectionPriority = priority;
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-int32_t Layer::getFrameRateSelectionPriority() const {
-    // Check if layer has priority set.
-    if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
-        return mDrawingState.frameRateSelectionPriority;
-    }
-    // If not, search whether its parents have it set.
-    sp<Layer> parent = getParent();
-    if (parent != nullptr) {
-        return parent->getFrameRateSelectionPriority();
-    }
-
-    return Layer::PRIORITY_UNSET;
-}
-
-bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) {
-    if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
-    mDrawingState.defaultFrameRateCompatibility = compatibility;
-    mDrawingState.modified = true;
-    mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
-    return mDrawingState.defaultFrameRateCompatibility;
-}
-
 bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
     return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
 };
@@ -1202,117 +1166,6 @@
     return StretchEffect{};
 }
 
-bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
-                                           bool* transactionNeeded) {
-    // Gets the frame rate to propagate to children.
-    const auto frameRate = [&] {
-        if (overrideChildren && parentFrameRate.isValid()) {
-            return parentFrameRate;
-        }
-
-        if (mDrawingState.frameRate.isValid()) {
-            return mDrawingState.frameRate;
-        }
-
-        return parentFrameRate;
-    }();
-
-    auto now = systemTime();
-    *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
-
-    // The frame rate is propagated to the children by default, but some properties may override it.
-    bool childrenHaveFrameRate = false;
-    const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate();
-    const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate;
-    for (const sp<Layer>& child : mCurrentChildren) {
-        childrenHaveFrameRate |=
-                child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate
-                                                                            : FrameRate(),
-                                                      overrideChildrenFrameRate, transactionNeeded);
-    }
-
-    // If we don't have a valid frame rate specification, but the children do, we set this
-    // layer as NoVote to allow the children to control the refresh rate
-    if (!frameRate.isValid() && childrenHaveFrameRate) {
-        *transactionNeeded |=
-                setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
-                                               now);
-    }
-
-    // We return whether this layer or 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.
-    const auto layerVotedWithDefaultCompatibility =
-            frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Default;
-    const auto layerVotedWithNoVote = frameRate.vote.type == FrameRateCompatibility::NoVote;
-    const auto layerVotedWithCategory = frameRate.category != FrameRateCategory::Default;
-    const auto layerVotedWithExactCompatibility =
-            frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Exact;
-    return layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory ||
-            layerVotedWithExactCompatibility || childrenHaveFrameRate;
-}
-
-void Layer::updateTreeHasFrameRateVote() {
-    const auto root = [&]() -> sp<Layer> {
-        sp<Layer> layer = sp<Layer>::fromExisting(this);
-        while (auto parent = layer->getParent()) {
-            layer = parent;
-        }
-        return layer;
-    }();
-
-    bool transactionNeeded = false;
-    root->propagateFrameRateForLayerTree({}, false, &transactionNeeded);
-
-    // TODO(b/195668952): we probably don't need eTraversalNeeded here
-    if (transactionNeeded) {
-        mFlinger->setTransactionFlags(eTraversalNeeded);
-    }
-}
-
-bool Layer::setFrameRate(FrameRate::FrameRateVote frameRateVote) {
-    if (mDrawingState.frameRate.vote == frameRateVote) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.frameRate.vote = frameRateVote;
-    mDrawingState.modified = true;
-
-    updateTreeHasFrameRateVote();
-
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool Layer::setFrameRateCategory(FrameRateCategory category, bool smoothSwitchOnly) {
-    if (mDrawingState.frameRate.category == category &&
-        mDrawingState.frameRate.categorySmoothSwitchOnly == smoothSwitchOnly) {
-        return false;
-    }
-
-    mDrawingState.sequence++;
-    mDrawingState.frameRate.category = category;
-    mDrawingState.frameRate.categorySmoothSwitchOnly = smoothSwitchOnly;
-    mDrawingState.modified = true;
-
-    updateTreeHasFrameRateVote();
-
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) {
-    if (mDrawingState.frameRateSelectionStrategy == strategy) return false;
-    mDrawingState.frameRateSelectionStrategy = strategy;
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-
-    updateTreeHasFrameRateVote();
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
 void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
                                                       nsecs_t postTime, gui::GameMode gameMode) {
     mDrawingState.postTime = postTime;
@@ -1446,25 +1299,6 @@
     addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
 }
 
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
-    if (mDrawingState.frameRateForLayerTree == frameRate) {
-        return false;
-    }
-
-    mDrawingState.frameRateForLayerTree = frameRate;
-
-    // TODO(b/195668952): we probably don't need to dirty visible regions here
-    // or even store frameRateForLayerTree in mDrawingState
-    mDrawingState.sequence++;
-    mDrawingState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-
-    mFlinger->mScheduler
-            ->recordLayerHistory(sequence, getLayerProps(), now, now,
-                                 scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
-    return true;
-}
-
 bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
                                      nsecs_t now) {
     if (mDrawingState.frameRateForLayerTree == frameRate) {
@@ -1543,57 +1377,6 @@
     result.append("\n");
 }
 
-void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(&display);
-    if (!outputLayer) {
-        return;
-    }
-
-    std::string name;
-    if (mName.length() > 77) {
-        std::string shortened;
-        shortened.append(mName, 0, 36);
-        shortened.append("[...]");
-        shortened.append(mName, mName.length() - 36);
-        name = std::move(shortened);
-    } else {
-        name = mName;
-    }
-
-    StringAppendF(&result, " %s\n", name.c_str());
-
-    const State& layerState(getDrawingState());
-    const auto& outputLayerState = outputLayer->getState();
-
-    if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
-        StringAppendF(&result, "  rel %6d | ", layerState.z);
-    } else {
-        StringAppendF(&result, "  %10d | ", layerState.z);
-    }
-    StringAppendF(&result, "  %10d | ", mWindowType);
-    StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
-    StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
-    const Rect& frame = outputLayerState.displayFrame;
-    StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
-    const FloatRect& crop = outputLayerState.sourceCrop;
-    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
-                  crop.bottom);
-    const auto frameRate = getFrameRateForLayerTree();
-    if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
-        StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
-                      ftl::enum_string(frameRate.vote.type).c_str(),
-                      ftl::enum_string(frameRate.vote.seamlessness).c_str());
-    } else {
-        result.append(41, ' ');
-    }
-
-    const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
-    StringAppendF(&result, "    [%s]\n", focused ? "*" : " ");
-
-    result.append(kDumpTableRowLength, '-');
-    result.append("\n");
-}
-
 void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
                      const DisplayDevice& display) const {
     const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
@@ -1679,7 +1462,6 @@
 
     mCurrentChildren.add(layer);
     layer->setParent(sp<Layer>::fromExisting(this));
-    updateTreeHasFrameRateVote();
 }
 
 ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1689,9 +1471,6 @@
     layer->setParent(nullptr);
     const auto removeResult = mCurrentChildren.remove(layer);
 
-    updateTreeHasFrameRateVote();
-    layer->updateTreeHasFrameRateVote();
-
     return removeResult;
 }
 
@@ -4013,7 +3792,8 @@
     }
 
     if (display) {
-        const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
+        const auto activeMode = display->refreshRateSelector().getActiveMode();
+        const Fps refreshRate = activeMode.fps;
         const std::optional<Fps> renderRate =
                 mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
 
@@ -4033,7 +3813,12 @@
                     mFlinger->getHwComposer().getPresentTimestamp(*displayId);
 
             const nsecs_t now = systemTime(CLOCK_MONOTONIC);
-            const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+            const nsecs_t vsyncPeriod =
+                    mFlinger->getHwComposer()
+                            .getDisplayVsyncPeriod(*displayId)
+                            .value_opt()
+                            .value_or(activeMode.modePtr->getVsyncRate().getPeriodNsecs());
+
             const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
 
             mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c838b97..948c62d 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -330,18 +330,10 @@
             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);
     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 FrameRateCompatibility getDefaultFrameRateCompatibility() const;
-    //
     ui::Dataspace getDataSpace() const;
 
     virtual bool isFrontBuffered() const;
@@ -702,7 +694,6 @@
     inline const State& getDrawingState() const { return mDrawingState; }
     inline State& getDrawingState() { return mDrawingState; }
 
-    void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
     void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
     void dumpOffscreenDebugInfo(std::string& result) const;
@@ -789,11 +780,6 @@
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
-    bool setFrameRate(FrameRate::FrameRateVote);
-    bool setFrameRateCategory(FrameRateCategory, bool smoothSwitchOnly);
-
-    bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
-
     virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
     void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime,
                                                    gui::GameMode gameMode);
@@ -905,19 +891,9 @@
     void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
                                    const sp<GraphicBuffer>& buffer, uint64_t framenumber,
                                    const sp<Fence>& releaseFence);
-    bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now);
     bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now);
     void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
     void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
-    auto getLayerProps() const {
-        return scheduler::LayerProps{.visible = isVisible(),
-                                     .bounds = getBounds(),
-                                     .transform = getTransform(),
-                                     .setFrameRateVote = getFrameRateForLayerTree(),
-                                     .frameRateSelectionPriority = getFrameRateSelectionPriority(),
-                                     .isSmallDirty = mSmallDirty,
-                                     .isFrontBuffered = isFrontBuffered()};
-    };
     bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
     void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
         mTransformHint = transformHint;
@@ -1128,7 +1104,6 @@
     LayerVector makeChildrenTraversalList(LayerVector::StateSet,
                                           const std::vector<Layer*>& layersInTree);
 
-    void updateTreeHasFrameRateVote();
     bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
                                         bool* transactionNeeded);
     void setZOrderRelativeOf(const wp<Layer>& relativeOf);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 4fd4c0e..64b85c0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -109,12 +109,12 @@
 
 LayerHistory::~LayerHistory() = default;
 
-void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled,
+                                 FrameRateCompatibility frameRateCompatibility) {
     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);
+    LayerVoteType type = getVoteType(frameRateCompatibility, 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
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index c09f148..e3babba 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -51,7 +51,8 @@
     ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, bool contentDetectionEnabled);
+    void registerLayer(Layer*, bool contentDetectionEnabled,
+                       FrameRateCompatibility frameRateCompatibility);
 
     // Sets the display size. Client is responsible for synchronization.
     void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 6b31135..727541e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -664,11 +664,12 @@
     }
 }
 
-void Scheduler::registerLayer(Layer* layer) {
+void Scheduler::registerLayer(Layer* layer, FrameRateCompatibility frameRateCompatibility) {
     // 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, mFeatures.test(Feature::kContentDetection));
+    mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection),
+                                frameRateCompatibility);
 }
 
 void Scheduler::deregisterLayer(Layer* layer) {
@@ -876,22 +877,19 @@
     mRefreshRateStats->dump(dumper.out());
     dumper.eol();
 
-    {
-        utils::Dumper::Section section(dumper, "Frame Targeting"sv);
+    std::scoped_lock lock(mDisplayLock);
+    ftl::FakeGuard guard(kMainThreadContext);
 
-        std::scoped_lock lock(mDisplayLock);
-        ftl::FakeGuard guard(kMainThreadContext);
+    for (const auto& [id, display] : mDisplays) {
+        utils::Dumper::Section
+                section(dumper,
+                        id == mPacesetterDisplayId
+                                ? ftl::Concat("Pacesetter Display ", id.value).c_str()
+                                : ftl::Concat("Follower Display ", id.value).c_str());
 
-        for (const auto& [id, display] : mDisplays) {
-            utils::Dumper::Section
-                    section(dumper,
-                            id == mPacesetterDisplayId
-                                    ? ftl::Concat("Pacesetter Display ", id.value).c_str()
-                                    : ftl::Concat("Follower Display ", id.value).c_str());
-
-            display.targeterPtr->dump(dumper);
-            dumper.eol();
-        }
+        display.selectorPtr->dump(dumper);
+        display.targeterPtr->dump(dumper);
+        dumper.eol();
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4003d9f..9b32fa9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -220,7 +220,7 @@
             REQUIRES(kMainThreadContext);
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
-    void registerLayer(Layer*);
+    void registerLayer(Layer*, FrameRateCompatibility);
     void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
                             nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9633cc5..9be9fee 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -205,8 +205,6 @@
 using ui::DisplayPrimaries;
 using ui::RenderIntent;
 
-using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
-
 namespace hal = android::hardware::graphics::composer::hal;
 
 namespace {
@@ -374,8 +372,6 @@
 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";
-
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
 bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -2187,14 +2183,6 @@
     static_cast<void>(mScheduler->schedule([this] { sample(); }));
 }
 
-nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
-    if (const auto display = getDefaultDisplayDeviceLocked()) {
-        return display->getVsyncPeriodFromHWC();
-    }
-
-    return 0;
-}
-
 void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
     if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
@@ -5724,7 +5712,7 @@
 }
 
 void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const {
-    StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
+    StringAppendF(&result, "%" PRId64 "\n", mScheduler->getPacesetterVsyncPeriod().ns());
     if (args.size() < 2) return;
 
     const auto name = String8(args[1]);
@@ -5784,11 +5772,6 @@
     // TODO(b/241285876): Move to DisplayModeController.
     dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
     dumper.eol();
-
-    StringAppendF(&result,
-                  "         present offset: %9" PRId64 " ns\t        VSYNC period: %9" PRId64
-                  " ns\n\n",
-                  dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
 }
 
 void SurfaceFlinger::dumpEvents(std::string& result) const {
@@ -6945,7 +6928,7 @@
 
     // Update the overlay on the main thread to avoid race conditions with
     // RefreshRateSelector::getActiveMode
-    static_cast<void>(mScheduler->schedule([=, this] {
+    static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
         const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
         if (!display) {
             ALOGW("%s: default display is null", __func__);
@@ -6953,15 +6936,9 @@
         }
         if (!display->isRefreshRateOverlayEnabled()) return;
 
-        const auto desiredModeIdOpt =
-                mDisplayModeController.getDesiredMode(display->getPhysicalId())
-                        .transform([](const display::DisplayModeRequest& request) {
-                            return request.mode.modePtr->getId();
-                        });
+        const auto state = mDisplayModeController.getKernelIdleTimerState(display->getPhysicalId());
 
-        const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
-        if (display->onKernelTimerChanged(desiredModeIdOpt, timerExpired)) {
+        if (display->onKernelTimerChanged(state.desiredModeIdOpt, state.isEnabled && expired)) {
             mScheduler->scheduleFrame();
         }
     }));
@@ -6983,8 +6960,8 @@
     }));
 }
 
-std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
+auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId)
+        -> std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> {
     const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
             android::Hwc2::Composer::OptionalFeature::KernelIdleTimer);
     const auto timeout = getIdleTimerTimeout(displayId);
@@ -7008,63 +6985,6 @@
     return {std::nullopt, timeout};
 }
 
-void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout,
-                                           KernelIdleTimerController controller,
-                                           PhysicalDisplayId displayId) {
-    switch (controller) {
-        case KernelIdleTimerController::HwcApi: {
-            getHwComposer().setIdleTimerEnabled(displayId, timeout);
-            break;
-        }
-        case KernelIdleTimerController::Sysprop: {
-            base::SetProperty(KERNEL_IDLE_TIMER_PROP, timeout > 0ms ? "true" : "false");
-            break;
-        }
-    }
-}
-
-void SurfaceFlinger::toggleKernelIdleTimer() {
-    using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
-
-    const auto display = getDefaultDisplayDeviceLocked();
-    if (!display) {
-        ALOGW("%s: default display is null", __func__);
-        return;
-    }
-
-    // If the support for kernel idle timer is disabled for the active display,
-    // don't do anything.
-    const std::optional<KernelIdleTimerController> kernelIdleTimerController =
-            display->refreshRateSelector().kernelIdleTimerController();
-    if (!kernelIdleTimerController.has_value()) {
-        return;
-    }
-
-    const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
-
-    switch (action) {
-        case KernelIdleTimerAction::TurnOff:
-            if (mKernelIdleTimerEnabled) {
-                SFTRACE_INT("KernelIdleTimer", 0);
-                std::chrono::milliseconds constexpr kTimerDisabledTimeout = 0ms;
-                updateKernelIdleTimer(kTimerDisabledTimeout, kernelIdleTimerController.value(),
-                                      display->getPhysicalId());
-                mKernelIdleTimerEnabled = false;
-            }
-            break;
-        case KernelIdleTimerAction::TurnOn:
-            if (!mKernelIdleTimerEnabled) {
-                SFTRACE_INT("KernelIdleTimer", 1);
-                const std::chrono::milliseconds timeout =
-                        display->refreshRateSelector().getIdleTimerTimeout();
-                updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
-                                      display->getPhysicalId());
-                mKernelIdleTimerEnabled = true;
-            }
-            break;
-    }
-}
-
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
@@ -7951,7 +7871,7 @@
 
     if (const bool isPacesetter =
                 mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
-        toggleKernelIdleTimer();
+        mDisplayModeController.updateKernelIdleTimer(displayId);
     }
 
     auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -8068,7 +7988,7 @@
 void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
     if (!layer->isRemovedFromCurrentState()) {
-        mScheduler->registerLayer(layer);
+        mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default);
     }
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8b71f3b..651f2d3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -707,22 +707,13 @@
     // ICEPowerCallback overrides:
     void notifyCpuLoadUp() 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>
             getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock);
-    // Updates the kernel idle timer either through HWC or through sysprop
-    // depending on which controller is provided
-    void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController,
-                               PhysicalDisplayId) REQUIRES(mStateLock);
-    // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
-    // make calls to sys prop each time.
-    bool mKernelIdleTimerEnabled = false;
+
     // Show spinner with refresh rate overlay
     bool mRefreshRateOverlaySpinner = false;
     // Show render rate with refresh rate overlay
@@ -1068,11 +1059,6 @@
             REQUIRES(mStateLock, kMainThreadContext);
 
     /*
-     * VSYNC
-     */
-    nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
-
-    /*
      * Display identification
      */
     sp<display::DisplayToken> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 8b9d14b..3104dd4 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -25,6 +25,7 @@
 #include <FrontEnd/LayerHierarchy.h>
 #include <FrontEnd/LayerLifecycleManager.h>
 #include <FrontEnd/LayerSnapshotBuilder.h>
+#include <Layer.h> // needed for framerate
 
 namespace android::surfaceflinger::frontend {
 
@@ -84,6 +85,15 @@
         mLifecycleManager.addLayers(std::move(layers));
     }
 
+    void createRootLayerWithUid(uint32_t id, gui::Uid uid) {
+        std::vector<std::unique_ptr<RequestedLayerState>> layers;
+        auto args = createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+                               /*mirror=*/UNASSIGNED_LAYER_ID);
+        args.ownerUid = uid.val();
+        layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+        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>(
@@ -321,6 +331,19 @@
         mLifecycleManager.applyTransactions(transactions);
     }
 
+    void setFrameRate(uint32_t id, Layer::FrameRate framerate) {
+        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().layerId = id;
+        transactions.back().states.front().state.frameRate = framerate.vote.rate.getValue();
+        transactions.back().states.front().state.frameRateCompatibility = 0;
+        transactions.back().states.front().state.changeFrameRateStrategy = 0;
+        mLifecycleManager.applyTransactions(transactions);
+    }
+
     void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
@@ -393,6 +416,14 @@
                                                                 GRALLOC_USAGE_PROTECTED /*usage*/));
     }
 
+    void setFrontBuffer(uint32_t id) {
+        static uint64_t sBufferId = 1;
+        setBuffer(id,
+                  std::make_shared<renderengine::mock::FakeExternalTexture>(
+                          1U /*width*/, 1U /*height*/, sBufferId++, HAL_PIXEL_FORMAT_RGBA_8888,
+                          GRALLOC_USAGE_PROTECTED | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/));
+    }
+
     void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
         std::vector<TransactionState> transactions;
         transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index d9d239d..f1bd87c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -80,13 +80,10 @@
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
         "FrameRateOverrideMappingsTest.cpp",
-        "FrameRateSelectionPriorityTest.cpp",
-        "FrameRateSelectionStrategyTest.cpp",
         "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
         "JankTrackerTest.cpp",
         "OneShotTimerTest.cpp",
-        "LayerHistoryTest.cpp",
         "LayerHistoryIntegrationTest.cpp",
         "LayerInfoTest.cpp",
         "LayerMetadataTest.cpp",
@@ -116,7 +113,6 @@
         "SurfaceFlinger_SetPowerModeInternalTest.cpp",
         "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
         "SchedulerTest.cpp",
-        "SetFrameRateTest.cpp",
         "RefreshRateSelectorTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
deleted file mode 100644
index d30d5b8..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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 <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::DoAll;
-using testing::Mock;
-using testing::Return;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-/**
- * This class covers all the test that are related to refresh rate selection.
- */
-class RefreshRateSelectionTest : public testing::Test {
-public:
-    RefreshRateSelectionTest();
-    ~RefreshRateSelectionTest() override;
-
-protected:
-    static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
-    static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
-    static constexpr uint32_t WIDTH = 100;
-    static constexpr uint32_t HEIGHT = 100;
-    static constexpr uint32_t LAYER_FLAGS = 0;
-    static constexpr int32_t PRIORITY_UNSET = -1;
-
-    sp<Layer> createBufferStateLayer();
-    sp<Layer> createEffectLayer();
-
-    void setParent(Layer* child, Layer* parent);
-    void commitTransaction(Layer* layer);
-
-    TestableSurfaceFlinger mFlinger;
-
-    sp<Client> mClient;
-    sp<Layer> mParent;
-    sp<Layer> mChild;
-    sp<Layer> mGrandChild;
-};
-
-RefreshRateSelectionTest::RefreshRateSelectionTest() {
-    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());
-
-    mFlinger.setupMockScheduler();
-    mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-RefreshRateSelectionTest::~RefreshRateSelectionTest() {
-    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());
-}
-
-sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
-    sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
-                           LayerMetadata());
-    return sp<Layer>::make(args);
-}
-
-sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
-    sp<Client> client;
-    LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
-    return sp<Layer>::make(args);
-}
-
-void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
-    child->setParent(sp<Layer>::fromExisting(parent));
-}
-
-void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
-    layer->commitTransaction();
-}
-
-namespace {
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
-    mParent = createBufferStateLayer();
-    mChild = createBufferStateLayer();
-    setParent(mChild.get(), mParent.get());
-    mGrandChild = createBufferStateLayer();
-    setParent(mGrandChild.get(), mChild.get());
-
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child has its own priority.
-    mGrandChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child inherits from his parent.
-    mChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Grandchild inherits from his grand parent.
-    mParent->setFrameRateSelectionPriority(1);
-    commitTransaction(mParent.get());
-    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) {
-    mParent = createEffectLayer();
-    mChild = createEffectLayer();
-    setParent(mChild.get(), mParent.get());
-    mGrandChild = createEffectLayer();
-    setParent(mGrandChild.get(), mChild.get());
-
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child has its own priority.
-    mGrandChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Child inherits from his parent.
-    mChild->setFrameRateSelectionPriority(1);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
-    // Grandchild inherits from his grand parent.
-    mParent->setFrameRateSelectionPriority(1);
-    commitTransaction(mParent.get());
-    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mChild.get());
-    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
-    commitTransaction(mGrandChild.get());
-    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
-    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
deleted file mode 100644
index 866eb08..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * 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 <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
-
-/**
- * This class tests the behaviour of Layer::setFrameRateSelectionStrategy.
- */
-class FrameRateSelectionStrategyTest : public BaseLayerTest {
-protected:
-    const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
-
-    FrameRateSelectionStrategyTest();
-
-    void addChild(sp<Layer> layer, sp<Layer> child);
-    void removeChild(sp<Layer> layer, sp<Layer> child);
-    void commitTransaction();
-
-    std::vector<sp<Layer>> mLayers;
-};
-
-FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() {
-    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());
-
-    mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) {
-    layer->addChild(child);
-}
-
-void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) {
-    layer->removeChild(child);
-}
-
-void FrameRateSelectionStrategyTest::commitTransaction() {
-    for (auto layer : mLayers) {
-        layer->commitTransaction();
-    }
-}
-
-namespace {
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest,
-                         testing::Values(std::make_shared<BufferStateLayerFactory>(),
-                                         std::make_shared<EffectLayerFactory>()),
-                         PrintToStringParamName);
-
-TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-    auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    layer->setFrameRate(FRAME_RATE_VOTE1.vote);
-    layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1.vote);
-    child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              parent->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              child1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              child2->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-    auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    addChild(layer1, layer2);
-    addChild(layer2, layer3);
-
-    layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
-    layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
-    layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
-    layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
-    layer3->setFrameRate(FRAME_RATE_VOTE3.vote);
-    commitTransaction();
-
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer2->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer3->getDrawingState().frameRateSelectionStrategy);
-
-    layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate);
-    commitTransaction();
-
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer2->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-    auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    addChild(layer1, layer2);
-    addChild(layer2, layer3);
-
-    layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
-    layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
-    layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self);
-    commitTransaction();
-
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Self,
-              layer2->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer3->getDrawingState().frameRateSelectionStrategy);
-
-    layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
-    commitTransaction();
-
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Self,
-              layer2->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer3->getDrawingState().frameRateSelectionStrategy);
-
-    layer1->setFrameRate(FRAME_RATE_DEFAULT.vote);
-    commitTransaction();
-
-    EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
-              layer1->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Self,
-              layer2->getDrawingState().frameRateSelectionStrategy);
-    EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
-    EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
-              layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-} // namespace
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a61fa1e..52bb07a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -38,6 +38,7 @@
 namespace android::scheduler {
 
 using android::mock::createDisplayMode;
+using android::mock::createVrrDisplayMode;
 using namespace com::android::graphics::surfaceflinger;
 
 class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
@@ -53,6 +54,8 @@
     static constexpr Fps HI_FPS = 90_Hz;
     static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
 
+    static constexpr auto kVrrModeId = DisplayModeId(2);
+
     LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
         mFlinger.resetScheduler(mScheduler);
         mLifecycleManager = {};
@@ -71,6 +74,13 @@
         updateLayerSnapshotsAndLayerHistory(time);
     }
 
+    void setFrontBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+        uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+        setFrontBuffer(sequence);
+        layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+        updateLayerSnapshotsAndLayerHistory(time);
+    }
+
     LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
     const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
 
@@ -135,6 +145,21 @@
         return layer;
     }
 
+    auto createLegacyAndFrontedEndLayerWithUid(uint32_t sequence, gui::Uid uid) {
+        std::string layerName = "test layer:" + std::to_string(sequence);
+        auto args = LayerCreationArgs{mFlinger.flinger(),
+                                      nullptr,
+                                      layerName,
+                                      0,
+                                      {},
+                                      std::make_optional<uint32_t>(sequence)};
+        args.ownerUid = uid.val();
+        const auto layer = sp<Layer>::make(args);
+        mFlinger.injectLegacyLayer(layer);
+        createRootLayerWithUid(sequence, uid);
+        return layer;
+    }
+
     auto destroyLayer(sp<Layer>& layer) {
         uint32_t sequence = static_cast<uint32_t>(layer->sequence);
         mFlinger.releaseLegacyLayer(sequence);
@@ -157,12 +182,13 @@
         ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
     }
 
-    std::shared_ptr<RefreshRateSelector> mSelector =
-            std::make_shared<RefreshRateSelector>(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),
+                      createVrrDisplayMode(kVrrModeId, HI_FPS,
+                                           hal::VrrConfig{.minFrameIntervalNs =
+                                                                  HI_FPS.getPeriodNsecs()})),
+            DisplayModeId(0));
 
     mock::SchedulerCallback mSchedulerCallback;
     TestableSurfaceFlinger mFlinger;
@@ -214,6 +240,146 @@
     EXPECT_EQ(1u, activeLayerCount());
 }
 
+TEST_F(LayerHistoryIntegrationTest, oneLayer) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    // Max returned if active layers have insufficient history.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        setBuffer(1);
+        updateLayerSnapshotsAndLayerHistory(time);
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        time += LO_FPS_PERIOD;
+    }
+
+    // Max is returned since we have enough history but there is no timestamp votes.
+    for (size_t i = 0; i < 10; i++) {
+        setBuffer(1);
+        updateLayerSnapshotsAndLayerHistory(time);
+        ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+        EXPECT_EQ(1u, activeLayerCount());
+        time += LO_FPS_PERIOD;
+    }
+}
+
+TEST_F(LayerHistoryIntegrationTest, gameFrameRateOverrideMapping) {
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+    history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
+
+    auto overridePair = history().getGameFrameRateOverride(0);
+    EXPECT_EQ(0_Hz, overridePair.first);
+    EXPECT_EQ(60_Hz, overridePair.second);
+
+    history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
+    history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
+
+    overridePair = history().getGameFrameRateOverride(0);
+    EXPECT_EQ(40_Hz, overridePair.first);
+    EXPECT_EQ(60_Hz, overridePair.second);
+
+    overridePair = history().getGameFrameRateOverride(1);
+    EXPECT_EQ(120_Hz, overridePair.first);
+    EXPECT_EQ(0_Hz, overridePair.second);
+
+    history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
+    history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
+
+    overridePair = history().getGameFrameRateOverride(0);
+    EXPECT_EQ(40_Hz, overridePair.first);
+    EXPECT_EQ(0_Hz, overridePair.second);
+
+    overridePair = history().getGameFrameRateOverride(1);
+    EXPECT_EQ(0_Hz, overridePair.first);
+    EXPECT_EQ(0_Hz, overridePair.second);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGameFrameRateOverride) {
+    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+    const uid_t uid = 0;
+    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+    const Fps gameModeFrameRate = Fps::fromValue(60.0f);
+
+    auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+    showLayer(1);
+
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    // update game default frame rate override
+    history().updateGameDefaultFrameRateOverride(
+            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+    LayerHistory::Summary summary;
+    scheduler::LayerProps layerProps = {
+            .visible = true,
+            .bounds = {0, 0, 100, 100},
+            .transform = {},
+            .setFrameRateVote = {},
+            .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+            .isSmallDirty = false,
+            .isFrontBuffered = false,
+    };
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += gameDefaultFrameRate.getPeriodNsecs();
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+    ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
+
+    // test against setFrameRate vote
+    setFrameRate(1,
+                 Layer::FrameRate(Fps::fromValue(120.0f), Layer::FrameRateCompatibility::Default));
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    const Fps setFrameRate = Fps::fromValue(120.0f);
+    layerProps.setFrameRateVote =
+            Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default);
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += setFrameRate.getPeriodNsecs();
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+    ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
+
+    // update game mode frame rate override
+    history().updateGameModeFrameRateOverride(
+            FrameRateOverride({uid, gameModeFrameRate.getValue()}));
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += gameModeFrameRate.getPeriodNsecs();
+        summary = summarizeLayerHistory(time);
+    }
+
+    ASSERT_EQ(1u, summary.size());
+    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+    ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
     createLegacyAndFrontedEndLayer(1);
     nsecs_t time = systemTime();
@@ -238,6 +404,110 @@
     EXPECT_EQ(0u, activeLayerCount());
 }
 
+TEST_F(LayerHistoryIntegrationTest, explicitTimestamp) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVote) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMinVote) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMaxVote) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(summarizeLayerHistory(time).empty());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
     createLegacyAndFrontedEndLayer(1);
     setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
@@ -273,6 +543,335 @@
     EXPECT_EQ(1, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote2) {
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became infrequent, but the vote stays
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) {
+    // Set the test to be on a vrr mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, 0);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+    // layer became inactive, but the vote stays
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Test for MRR device with VRR features enabled.
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+    // The vrr_config flag is explicitly not set false because this test for an MRR device
+    // should still work in a VRR-capable world.
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, 0);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+    // layer became infrequent, but the vote stays
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+    // Set default to Min so it is obvious that the vote reset triggered.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
+// the category is NoPreference.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became infrequent
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
+    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // There are 2 LayerRequirement's due to the frame rate category.
+    ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+    // First LayerRequirement is the layer's category specification
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+
+    // Second LayerRequirement is the frame rate specification
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
+    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
+
+    // layer became infrequent, but the vote stays
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
+    SET_FLAG_FOR_TEST(flags::misc1, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    hideLayer(1);
+    setFrameRate(1, (12.34_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
+    // votes to consider for refresh rate selection.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+    SET_FLAG_FOR_TEST(flags::misc1, false);
+
+    auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+    auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+    hideLayer(2);
+    setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    setBuffer(1);
+    setBuffer(2);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(2u, layerCount());
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2u, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayerDoesNotVote) {
+    SET_FLAG_FOR_TEST(flags::misc1, true);
+
+    auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+    auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+    hideLayer(2);
+    setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    setBuffer(1);
+    setBuffer(2);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    EXPECT_EQ(2u, layerCount());
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frontBufferedLayerVotesMax) {
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    setFrontBuffer(1);
+    showLayer(1);
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setFrontBufferWithPresentTime(layer, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // Layer still active due to front buffering, but it's infrequent.
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
 
@@ -805,7 +1404,15 @@
 
     // layer is active but infrequent.
     for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        auto props = layer->getLayerProps();
+        scheduler::LayerProps props = {
+                .visible = false,
+                .bounds = {0, 0, 100, 100},
+                .transform = {},
+                .setFrameRateVote = {},
+                .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+                .isSmallDirty = false,
+                .isFrontBuffered = false,
+        };
         if (i % 3 == 0) {
             props.isSmallDirty = false;
         } else {
@@ -838,8 +1445,15 @@
 
     // uiLayer is updating small dirty.
     for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
-        auto props = uiLayer->getLayerProps();
-        props.isSmallDirty = true;
+        scheduler::LayerProps props = {
+                .visible = false,
+                .bounds = {0, 0, 100, 100},
+                .transform = {},
+                .setFrameRateVote = {},
+                .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+                .isSmallDirty = true,
+                .isFrontBuffered = false,
+        };
         setBuffer(1);
         uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
         updateLayerSnapshotsAndLayerHistory(time);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
deleted file mode 100644
index 088d0d2..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ /dev/null
@@ -1,1573 +0,0 @@
-/*
- * 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTest"
-
-#include <Layer.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include <common/test/FlagUtils.h>
-#include "FpsOps.h"
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfo.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/MockLayer.h"
-#include "mock/MockSchedulerCallback.h"
-
-using testing::_;
-using testing::Return;
-using testing::ReturnRef;
-
-namespace android::scheduler {
-
-using MockLayer = android::mock::MockLayer;
-
-using android::mock::createDisplayMode;
-using android::mock::createVrrDisplayMode;
-
-// WARNING: LEGACY TESTS FOR LEGACY FRONT END
-// Update LayerHistoryIntegrationTest instead
-class LayerHistoryTest : public testing::Test {
-protected:
-    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
-    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 Fps LO_FPS = 30_Hz;
-    static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
-
-    static constexpr Fps HI_FPS = 90_Hz;
-    static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
-
-    LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
-
-    LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
-    const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
-
-    LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
-        // 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->refreshRateSelector(), now);
-        std::sort(summary.begin(), summary.end(),
-                  [](const RefreshRateSelector::LayerRequirement& lhs,
-                     const RefreshRateSelector::LayerRequirement& rhs) -> bool {
-                      return lhs.name < rhs.name;
-                  });
-        return summary;
-    }
-
-    size_t layerCount() const { return mScheduler->layerHistorySize(); }
-    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
-        return history().mActiveLayerInfos.size();
-    }
-
-    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).isFrequent;
-        });
-    }
-
-    auto animatingLayerCount(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->isAnimating(now);
-        });
-    }
-
-    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());
-        if (found != LayerHistory::LayerStatus::NotFound) {
-            layerPair->second->setDefaultLayerVote(vote);
-        }
-    }
-
-    auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
-    auto createLayer(std::string name) {
-        return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
-    }
-    auto createLayer(std::string name, uint32_t uid) {
-        return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid));
-    }
-
-    void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
-                               Fps desiredRefreshRate, int numFrames) {
-        LayerHistory::Summary summary;
-        for (int i = 0; i < numFrames; i++) {
-            history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                             LayerHistory::LayerUpdateType::Buffer);
-            time += frameRate.getPeriodNsecs();
-
-            summary = summarizeLayerHistory(time);
-        }
-
-        ASSERT_EQ(1, summary.size());
-        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-        ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
-    }
-
-    static constexpr auto kVrrModeId = DisplayModeId(2);
-    std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
-            makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
-                      createDisplayMode(DisplayModeId(1), HI_FPS),
-                      createVrrDisplayMode(kVrrModeId, HI_FPS,
-                                           hal::VrrConfig{.minFrameIntervalNs =
-                                                                  HI_FPS.getPeriodNsecs()})),
-            DisplayModeId(0));
-
-    mock::SchedulerCallback mSchedulerCallback;
-    TestableSurfaceFlinger mFlinger;
-    TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback);
-};
-
-namespace {
-
-using namespace com::android::graphics::surfaceflinger;
-
-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(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->getSequence(),
-
-                                               layer->getDefaultFrameRateCompatibility(),
-                                               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(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->getSequence(),
-                                               layer->getDefaultFrameRateCompatibility(),
-                                               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));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
-
-    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());
-
-    // Max returned if active layers have insufficient history.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        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());
-        time += LO_FPS_PERIOD;
-    }
-
-    // Max is returned since we have enough history but there is no timestamp votes.
-    for (int i = 0; i < 10; i++) {
-        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());
-        time += LO_FPS_PERIOD;
-    }
-}
-
-TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) {
-    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
-    history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
-
-    auto overridePair = history().getGameFrameRateOverride(0);
-    EXPECT_EQ(0_Hz, overridePair.first);
-    EXPECT_EQ(60_Hz, overridePair.second);
-
-    history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
-    history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
-
-    overridePair = history().getGameFrameRateOverride(0);
-    EXPECT_EQ(40_Hz, overridePair.first);
-    EXPECT_EQ(60_Hz, overridePair.second);
-
-    overridePair = history().getGameFrameRateOverride(1);
-    EXPECT_EQ(120_Hz, overridePair.first);
-    EXPECT_EQ(0_Hz, overridePair.second);
-
-    history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
-    history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
-
-    overridePair = history().getGameFrameRateOverride(0);
-    EXPECT_EQ(40_Hz, overridePair.first);
-    EXPECT_EQ(0_Hz, overridePair.second);
-
-    overridePair = history().getGameFrameRateOverride(1);
-    EXPECT_EQ(0_Hz, overridePair.first);
-    EXPECT_EQ(0_Hz, overridePair.second);
-}
-
-TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) {
-    SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
-    const uid_t uid = 0;
-    const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
-    const Fps gameModeFrameRate = Fps::fromValue(60.0f);
-    const auto layer = createLayer("GameFrameRateLayer", uid);
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-    EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    // update game default frame rate override
-    history().updateGameDefaultFrameRateOverride(
-            FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
-
-    nsecs_t time = systemTime();
-    LayerHistory::Summary summary;
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += gameDefaultFrameRate.getPeriodNsecs();
-
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
-    ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
-
-    // test against setFrameRate vote
-    const Fps setFrameRate = Fps::fromValue(120.0f);
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default)));
-
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += setFrameRate.getPeriodNsecs();
-
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
-    ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
-
-    // update game mode frame rate override
-    history().updateGameModeFrameRateOverride(
-            FrameRateOverride({uid, gameModeFrameRate.getValue()}));
-
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += gameModeFrameRate.getPeriodNsecs();
-
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
-    ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, oneInvisibleLayer) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-
-    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
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-    history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-
-    summary = summarizeLayerHistory(time);
-    EXPECT_TRUE(summarizeLayerHistory(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, explicitTimestamp) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerNoVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_TRUE(summarizeLayerHistory(time).empty());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(summarizeLayerHistory(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMinVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(summarizeLayerHistory(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMaxVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(summarizeLayerHistory(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became infrequent, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became infrequent, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_vrr) {
-    // Set the test to be on a vrr mode.
-    SET_FLAG_FOR_TEST(flags::vrr_config, true);
-    mSelector->setActiveMode(kVrrModeId, HI_FPS);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
-                                                    Seamlessness::OnlySeamless,
-                                                    FrameRateCategory::Default)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
-    // layer became inactive, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-// Test for MRR device with VRR features enabled.
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_nonVrr) {
-    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-    // The vrr_config flag is explicitly not set false because this test for an MRR device
-    // should still work in a VRR-capable world.
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
-                                                    Seamlessness::OnlySeamless,
-                                                    FrameRateCategory::Default)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
-    // layer became infrequent, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
-    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
-                                            Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
-    // Set default to Min so it is obvious that the vote reset triggered.
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitCategory) {
-    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
-                                            Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-    // First LayerRequirement is the frame rate specification
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-
-    // layer became infrequent, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
-// the category is NoPreference.
-TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) {
-    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
-                                                    Seamlessness::OnlySeamless,
-                                                    FrameRateCategory::NoPreference)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    EXPECT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became infrequent
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    EXPECT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) {
-    SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
-                                            Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    // There are 2 LayerRequirement's due to the frame rate category.
-    ASSERT_EQ(2, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-    // First LayerRequirement is the layer's category specification
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-
-    // Second LayerRequirement is the frame rate specification
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
-    EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
-
-    // layer became infrequent, but the vote stays
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(2, summarizeLayerHistory(time).size());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
-    SET_FLAG_FOR_TEST(flags::misc1, true);
-
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(12.34_Hz, Layer::FrameRateCompatibility::Default,
-                                            Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
-    // votes to consider for refresh rate selection.
-    ASSERT_EQ(0, summarizeLayerHistory(time).size());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, multipleLayers) {
-    auto layer1 = createLayer("A");
-    auto layer2 = createLayer("B");
-    auto layer3 = createLayer("C");
-
-    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    EXPECT_EQ(3, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    LayerHistory::Summary summary;
-
-    // layer1 is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // layer2 is frequent and has high refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        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->getSequence(), layer1->getLayerProps(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
-    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
-
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // 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->getSequence(), layer2->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer2 still has low refresh rate.
-    // layer3 has high refresh rate but not enough history.
-    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->getSequence(), layer2->getLayerProps(), time, time,
-                             LayerHistory::LayerUpdateType::Buffer);
-        }
-
-        history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer3 becomes recently active.
-    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);
-    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer1 expires.
-    layer1.clear();
-    summary = summarizeLayerHistory(time);
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
-    EXPECT_EQ(2, layerCount());
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer2 still has low refresh rate.
-    // layer3 becomes inactive.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer2 expires.
-    layer2.clear();
-    summary = summarizeLayerHistory(time);
-    EXPECT_TRUE(summary.empty());
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // 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->getSequence(), layer3->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer3 expires.
-    layer3.clear();
-    summary = summarizeLayerHistory(time);
-    EXPECT_TRUE(summary.empty());
-    EXPECT_EQ(0, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, inactiveLayers) {
-    auto layer = createLayer();
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    // the very first updates makes the layer frequent
-    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-        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));
-    }
-
-    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    EXPECT_EQ(1, layerCount());
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // advance the time for the previous frame to be inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-
-    // 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->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-
-        EXPECT_EQ(1, layerCount());
-        ASSERT_EQ(1, summarizeLayerHistory(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
-        EXPECT_EQ(1, activeLayerCount());
-        EXPECT_EQ(0, frequentLayerCount(time));
-    }
-
-    // More quick frames will get us to frequent again
-    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-    time += HI_FPS_PERIOD;
-
-    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));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
-    SET_FLAG_FOR_TEST(flags::misc1, false);
-
-    auto explicitVisiblelayer = createLayer();
-    auto explicitInvisiblelayer = createLayer();
-
-    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    nsecs_t time = systemTime();
-
-    // Post a buffer to the layers to make them active
-    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());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayerDoesNotVote) {
-    SET_FLAG_FOR_TEST(flags::misc1, true);
-
-    auto explicitVisiblelayer = createLayer();
-    auto explicitInvisiblelayer = createLayer();
-
-    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    nsecs_t time = systemTime();
-
-    // Post a buffer to the layers to make them active
-    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());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
-    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));
-
-    // layer is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-    }
-
-    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));
-
-    // another update with the same cadence keep in infrequent
-    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    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));
-
-    // an update as animation will immediately vote for Max
-    history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                     LayerHistory::LayerUpdateType::AnimationTX);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) {
-    SET_FLAG_FOR_TEST(flags::vrr_config, true);
-    auto layer = createLayer();
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-    EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true));
-
-    nsecs_t time = systemTime();
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, animatingLayerCount(time));
-
-    // layer is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-    }
-
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, animatingLayerCount(time));
-
-    // Layer still active due to front buffering, but it's infrequent.
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_EQ(1, summarizeLayerHistory(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, 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();
-
-    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));
-
-    // layer is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer->getSequence(), layer->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-    }
-
-    float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
-    EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
-}
-
-TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
-        recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-    }
-}
-
-TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-
-    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
-    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
-
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
-    SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
-
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyLayer) {
-    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));
-
-    LayerHistory::Summary summary;
-
-    // layer is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        auto props = layer->getLayerProps();
-        if (i % 3 == 0) {
-            props.isSmallDirty = false;
-        } else {
-            props.isSmallDirty = true;
-        }
-
-        history().record(layer->getSequence(), props, time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyInMultiLayer) {
-    auto layer1 = createLayer("UI");
-    auto layer2 = createLayer("Video");
-
-    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(30_Hz, Layer::FrameRateCompatibility::Default)));
-
-    nsecs_t time = systemTime();
-
-    EXPECT_EQ(2, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    LayerHistory::Summary summary;
-
-    // layer1 is updating small dirty.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
-        auto props = layer1->getLayerProps();
-        props.isSmallDirty = true;
-        history().record(layer1->getSequence(), props, 0 /*presentTime*/, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
-                         LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = summarizeLayerHistory(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
-    ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
-}
-
-class LayerHistoryTestParameterized : public LayerHistoryTest,
-                                      public testing::WithParamInterface<std::chrono::nanoseconds> {
-};
-
-TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
-    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
-    auto heuristicLayer = createLayer("HeuristicLayer");
-
-    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate()));
-
-    auto infrequentLayer = createLayer("InfrequentLayer");
-    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate()));
-
-    const nsecs_t startTime = systemTime();
-
-    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
-    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;
-    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
-    int infrequentLayerUpdates = 0;
-    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
-        time += heuristicUpdateDelta.count();
-        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->getSequence(), infrequentLayer->getLayerProps(), time,
-                             time, LayerHistory::LayerUpdateType::Buffer);
-            infrequentLayerUpdates++;
-        }
-
-        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
-            ASSERT_NE(0, summarizeLayerHistory(time).size());
-            ASSERT_GE(2, summarizeLayerHistory(time).size());
-
-            bool max = false;
-            bool min = false;
-            Fps heuristic;
-            for (const auto& layer : summarizeLayerHistory(time)) {
-                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
-                    heuristic = layer.desiredRefreshRate;
-                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
-                    max = true;
-                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
-                    min = true;
-                }
-            }
-
-            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
-                EXPECT_EQ(24_Hz, heuristic);
-                EXPECT_FALSE(max);
-                if (summarizeLayerHistory(time).size() == 2) {
-                    EXPECT_TRUE(min);
-                }
-            }
-        }
-    }
-}
-
-INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized,
-                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // 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/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 2bb864a..2860345 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -56,6 +56,17 @@
 
 class LayerSnapshotTest : public LayerSnapshotTestBase {
 protected:
+    const Layer::FrameRate FRAME_RATE_VOTE1 =
+            Layer::FrameRate(67_Hz, scheduler::FrameRateCompatibility::Default);
+    const Layer::FrameRate FRAME_RATE_VOTE2 =
+            Layer::FrameRate(14_Hz, scheduler::FrameRateCompatibility::Default);
+    const Layer::FrameRate FRAME_RATE_VOTE3 =
+            Layer::FrameRate(99_Hz, scheduler::FrameRateCompatibility::Default);
+    const Layer::FrameRate FRAME_RATE_TREE =
+            Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::NoVote);
+    const Layer::FrameRate FRAME_RATE_NO_VOTE =
+            Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::Default);
+
     LayerSnapshotTest() : LayerSnapshotTestBase() {
         UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
     }
@@ -787,6 +798,155 @@
               scheduler::FrameRateCompatibility::Default);
 }
 
+TEST_F(LayerSnapshotTest, frameRateSetAndGet) {
+    setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    // verify parent is gets no vote
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParent) {
+    setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParentAllVote) {
+    setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+    setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+    setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE2);
+
+    setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE3);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE3);
+
+    setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChild) {
+    setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAllVote) {
+    setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+    setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+    setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAddAfterVote) {
+    setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    reparentLayer(111, 2);
+    std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+    reparentLayer(111, 11);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildRemoveAfterVote) {
+    setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+    reparentLayer(111, 2);
+    std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+    UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+    setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateAddChildForParentWithTreeVote) {
+    setFrameRate(11, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+
+    setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
 TEST_F(LayerSnapshotTest, translateDataspace) {
     setDataspace(1, ui::Dataspace::UNKNOWN);
     UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -965,7 +1125,7 @@
     EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
     EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
               scheduler::FrameRateCompatibility::NoVote);
-    EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+    EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
 
     // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
     EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
@@ -1056,7 +1216,7 @@
     EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
               scheduler::FrameRateCompatibility::NoVote);
     EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
-    EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+    EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
 
     // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
     EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index dddda05..358f6b0 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -168,7 +168,17 @@
 
     // recordLayerHistory should be a noop
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+    scheduler::LayerProps layerProps = {
+            .visible = true,
+            .bounds = {0, 0, 100, 100},
+            .transform = {},
+            .setFrameRateVote = {},
+            .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+            .isSmallDirty = false,
+            .isFrontBuffered = false,
+    };
+
+    mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
                                    LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
 
@@ -194,7 +204,16 @@
                                                                       kDisplay1Mode60->getId()));
 
     ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+    scheduler::LayerProps layerProps = {
+            .visible = true,
+            .bounds = {0, 0, 100, 100},
+            .transform = {},
+            .setFrameRateVote = {},
+            .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+            .isSmallDirty = false,
+            .isFrontBuffered = false,
+    };
+    mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
                                    LayerHistory::LayerUpdateType::Buffer);
     ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
 }
@@ -259,9 +278,16 @@
                                                                       kDisplay1Mode60->getId()));
 
     const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
-    EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
-
-    mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
+    scheduler::LayerProps layerProps = {
+            .visible = true,
+            .bounds = {0, 0, 0, 0},
+            .transform = {},
+            .setFrameRateVote = {},
+            .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+            .isSmallDirty = false,
+            .isFrontBuffered = false,
+    };
+    mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, systemTime(),
                                    LayerHistory::LayerUpdateType::Buffer);
 
     constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
deleted file mode 100644
index 4705dd1..0000000
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * 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 <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/FrameRateUtils.h>
-#include <gui/LayerMetadata.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include "Layer.h"
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-#include "FpsOps.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockVsyncController.h"
-
-namespace android {
-using testing::_;
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-
-/**
- * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
- */
-class SetFrameRateTest : public BaseLayerTest {
-protected:
-    const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
-    const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
-    const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote);
-    const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
-    const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default);
-
-    SetFrameRateTest();
-
-    void addChild(sp<Layer> layer, sp<Layer> child);
-    void removeChild(sp<Layer> layer, sp<Layer> child);
-    void commitTransaction();
-
-    std::vector<sp<Layer>> mLayers;
-};
-
-SetFrameRateTest::SetFrameRateTest() {
-    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());
-
-    mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
-    layer->addChild(child);
-}
-
-void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
-    layer->removeChild(child);
-}
-
-void SetFrameRateTest::commitTransaction() {
-    for (auto layer : mLayers) {
-        layer->commitTransaction();
-    }
-}
-
-namespace {
-
-TEST_P(SetFrameRateTest, SetAndGet) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    layer->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParent) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1.vote);
-    child1->setFrameRate(FRAME_RATE_VOTE2.vote);
-    parent->setFrameRate(FRAME_RATE_VOTE3.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree());
-
-    child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree());
-
-    parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChild) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    parent->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1.vote);
-    child1->setFrameRate(FRAME_RATE_VOTE2.vote);
-    parent->setFrameRate(FRAME_RATE_VOTE3.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-
-    parent->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
-    addChild(child1, child2);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-
-    parent->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
-    removeChild(child1, child2);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
-    parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, child2);
-    addChild(child1, child2_1);
-
-    child2->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-
-    child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-}
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
-                         testing::Values(std::make_shared<BufferStateLayerFactory>(),
-                                         std::make_shared<EffectLayerFactory>()),
-                         PrintToStringParamName);
-
-TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
-    const auto& layerFactory = GetParam();
-
-    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    addChild(parent, child);
-
-    parent->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-
-    auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
-    history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
-                   LayerHistory::LayerUpdateType::Buffer);
-    history.record(child->getSequence(), child->getLayerProps(), 0, 0,
-                   LayerHistory::LayerUpdateType::Buffer);
-
-    const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
-    const auto summary = history.summarize(*selectorPtr, 0);
-
-    ASSERT_EQ(2u, summary.size());
-    EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[0].desiredRefreshRate);
-    EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[1].desiredRefreshRate);
-}
-
-TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
-    EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
-
-    const auto& layerFactory = GetParam();
-
-    const auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    const auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    const auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-    const auto childOfChild1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
-    addChild(parent, child1);
-    addChild(child1, childOfChild1);
-
-    childOfChild1->setFrameRate(FRAME_RATE_VOTE1.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
-    addChild(parent, child2);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
-    childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
-    commitTransaction();
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, childOfChild1->getFrameRateForLayerTree());
-    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 184dada..e380e19 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -180,6 +180,8 @@
     MOCK_METHOD1(onHotplugDisconnect, void(Display));
     MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
     MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
+    MOCK_METHOD(Error, getDisplayLuts,
+                (Display, std::vector<aidl::android::hardware::graphics::composer3::Lut>*));
 };
 
 } // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 602bdfc..1eda358 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -109,6 +109,8 @@
     MOCK_METHOD(hal::Error, getOverlaySupport,
                 (aidl::android::hardware::graphics::composer3::OverlayProperties *),
                 (const override));
+    MOCK_METHOD(hal::Error, getDisplayLuts,
+                (std::vector<aidl::android::hardware::graphics::composer3::Lut>*), (override));
 };
 
 class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 002fa9f..fdb6f4d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,16 +24,10 @@
 class MockLayer : public Layer {
 public:
     MockLayer(SurfaceFlinger* flinger, std::string name)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
-        EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
-                .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
-    }
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
 
     MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
-          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {
-        EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
-                .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
-    }
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
 
     explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}