SF: add render frame rate to the scheduler

Schedule SF at the rate of the render frame rate instead of
the display refresh rate.

Test: SF unit tests
Bug: 257072060
Change-Id: Idaf9be5f25373d38c0ef6440f9f401dc90de7a91
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 982b9ff..60ad7a3 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
 #include "DisplayTransactionTestHelpers.h"
+#include "mock/MockFrameRateMode.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -29,6 +30,7 @@
 
 class InitiateModeChangeTest : public DisplayTransactionTest {
 public:
+    using Action = DisplayDevice::DesiredActiveModeAction;
     using Event = scheduler::DisplayModeEvent;
 
     void SetUp() override {
@@ -56,31 +58,42 @@
     static constexpr DisplayModeId kModeId90{1};
     static constexpr DisplayModeId kModeId120{2};
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
 };
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setCurrentMode) {
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode60, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{60_Hz, kMode60}, Event::None}));
     EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
 TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    // Setting another mode should be cached but return false
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    // Setting another mode should be cached but return None
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 }
 
 TEST_F(InitiateModeChangeTest, clearDesiredActiveModeState) {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
 
     mDisplay->clearDesiredActiveModeState();
@@ -88,9 +101,11 @@
 }
 
 TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -101,18 +116,27 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
     ASSERT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
 }
 
+TEST_F(InitiateModeChangeTest, initiateRenderRateChange) {
+    EXPECT_EQ(Action::InitiateRenderRateSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{30_Hz, kMode60}, Event::None}));
+    EXPECT_EQ(std::nullopt, mDisplay->getDesiredActiveMode());
+}
+
 TEST_F(InitiateModeChangeTest, getUpcomingActiveMode_desiredActiveModeChanged)
 NO_THREAD_SAFETY_ANALYSIS {
-    EXPECT_TRUE(mDisplay->setDesiredActiveMode({kMode90, Event::None}));
+    EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode90, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
     hal::VsyncPeriodChangeConstraints constraints{
@@ -123,21 +147,23 @@
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
-    EXPECT_FALSE(mDisplay->setDesiredActiveMode({kMode120, Event::None}));
+    EXPECT_EQ(Action::None,
+              mDisplay->setDesiredActiveMode(
+                      {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
     ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
-    EXPECT_EQ(kMode120, mDisplay->getDesiredActiveMode()->mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
 
-    EXPECT_EQ(kMode90, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     EXPECT_EQ(OK,
               mDisplay->initiateModeChange(*mDisplay->getDesiredActiveMode(), constraints,
                                            &timeline));
-    EXPECT_EQ(kMode120, mDisplay->getUpcomingActiveMode().mode);
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
     EXPECT_EQ(Event::None, mDisplay->getUpcomingActiveMode().event);
 
     mDisplay->clearDesiredActiveModeState();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 8757e63..fdf2ffe 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -31,6 +31,9 @@
 #include "FpsOps.h"
 #include "Scheduler/RefreshRateSelector.h"
 #include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include "libsurfaceflinger_unittest_main.h"
 
 using namespace std::chrono_literals;
 
@@ -45,44 +48,41 @@
 
 using mock::createDisplayMode;
 
-// Use a C style macro to keep the line numbers printed in gtest
-#define EXPECT_SCORED_FRAME_RATE(modePtr, fps, scored) \
-    EXPECT_EQ((FrameRateMode{(fps), (modePtr)}), (scored).frameRateMode)
-
 struct TestableRefreshRateSelector : RefreshRateSelector {
     using RefreshRateSelector::FrameRateRanking;
     using RefreshRateSelector::RefreshRateOrder;
 
     using RefreshRateSelector::RefreshRateSelector;
 
-    void setActiveModeId(DisplayModeId modeId) {
+    void setActiveMode(DisplayModeId modeId, Fps renderFrameRate) {
         ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateSelector::setActiveModeId(modeId);
+        return RefreshRateSelector::setActiveMode(modeId, renderFrameRate);
     }
 
     const DisplayMode& getActiveMode() const {
-        ftl::FakeGuard guard(kMainThreadContext);
-        return RefreshRateSelector::getActiveMode();
+        std::lock_guard lock(mLock);
+        return *RefreshRateSelector::getActiveModeLocked().modePtr;
     }
 
-    DisplayModePtr getMinSupportedRefreshRate() const {
+    ftl::NonNull<DisplayModePtr> getMinSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return mMinRefreshRateModeIt->second;
+        return ftl::as_non_null(mMinRefreshRateModeIt->second);
     }
 
-    DisplayModePtr getMaxSupportedRefreshRate() const {
+    ftl::NonNull<DisplayModePtr> getMaxSupportedRefreshRate() const {
         std::lock_guard lock(mLock);
-        return mMaxRefreshRateModeIt->second;
+        return ftl::as_non_null(mMaxRefreshRateModeIt->second);
     }
 
-    DisplayModePtr getMinRefreshRateByPolicy() const {
+    ftl::NonNull<DisplayModePtr> getMinRefreshRateByPolicy() const {
         std::lock_guard lock(mLock);
-        return getMinRefreshRateByPolicyLocked();
+        return ftl::as_non_null(getMinRefreshRateByPolicyLocked());
     }
 
-    DisplayModePtr getMaxRefreshRateByPolicy() const {
+    ftl::NonNull<DisplayModePtr> getMaxRefreshRateByPolicy() const {
         std::lock_guard lock(mLock);
-        return getMaxRefreshRateByPolicyLocked(getActiveModeItLocked()->second->getGroup());
+        return ftl::as_non_null(
+                getMaxRefreshRateByPolicyLocked(getActiveModeLocked().modePtr->getGroup()));
     }
 
     FrameRateRanking rankRefreshRates(std::optional<int> anchorGroupOpt,
@@ -112,8 +112,8 @@
         return std::make_pair(ranking, consideredSignals);
     }
 
-    DisplayModePtr getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
-                                        GlobalSignals signals = {}) const {
+    ftl::NonNull<DisplayModePtr> getBestFrameRateMode(
+            const std::vector<LayerRequirement>& layers = {}, GlobalSignals signals = {}) const {
         return getRankedFrameRates(layers, signals).ranking.front().frameRateMode.modePtr;
     }
 
@@ -154,25 +154,42 @@
     static constexpr DisplayModeId kModeId60Frac{10};
     static constexpr DisplayModeId kModeId35{11};
 
-    static inline const DisplayModePtr kMode60 = createDisplayMode(kModeId60, 60_Hz);
-    static inline const DisplayModePtr kMode60Frac = createDisplayMode(kModeId60Frac, 59.94_Hz);
-    static inline const DisplayModePtr kMode90 = createDisplayMode(kModeId90, 90_Hz);
-    static inline const DisplayModePtr kMode90_G1 = createDisplayMode(kModeId90, 90_Hz, 1);
-    static inline const DisplayModePtr kMode90_4K =
-            createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160});
-    static inline const DisplayModePtr kMode72 = createDisplayMode(kModeId72, 72_Hz);
-    static inline const DisplayModePtr kMode72_G1 = createDisplayMode(kModeId72, 72_Hz, 1);
-    static inline const DisplayModePtr kMode120 = createDisplayMode(kModeId120, 120_Hz);
-    static inline const DisplayModePtr kMode120_G1 = createDisplayMode(kModeId120, 120_Hz, 1);
-    static inline const DisplayModePtr kMode30 = createDisplayMode(kModeId30, 30_Hz);
-    static inline const DisplayModePtr kMode30_G1 = createDisplayMode(kModeId30, 30_Hz, 1);
-    static inline const DisplayModePtr kMode30Frac = createDisplayMode(kModeId30Frac, 29.97_Hz);
-    static inline const DisplayModePtr kMode25 = createDisplayMode(kModeId25, 25_Hz);
-    static inline const DisplayModePtr kMode25_G1 = createDisplayMode(kModeId25, 25_Hz, 1);
-    static inline const DisplayModePtr kMode35 = createDisplayMode(kModeId35, 35_Hz);
-    static inline const DisplayModePtr kMode50 = createDisplayMode(kModeId50, 50_Hz);
-    static inline const DisplayModePtr kMode24 = createDisplayMode(kModeId24, 24_Hz);
-    static inline const DisplayModePtr kMode24Frac = createDisplayMode(kModeId24Frac, 23.976_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+            ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode60Frac =
+            ftl::as_non_null(createDisplayMode(kModeId60Frac, 59.94_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode90_4K =
+            ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz, 0, {3840, 2160}));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode72_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId72, 72_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode120_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId30, 30_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode30Frac =
+            ftl::as_non_null(createDisplayMode(kModeId30Frac, 29.97_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode25_G1 =
+            ftl::as_non_null(createDisplayMode(kModeId25, 25_Hz, 1));
+    static inline const ftl::NonNull<DisplayModePtr> kMode35 =
+            ftl::as_non_null(createDisplayMode(kModeId35, 35_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode50 =
+            ftl::as_non_null(createDisplayMode(kModeId50, 50_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24 =
+            ftl::as_non_null(createDisplayMode(kModeId24, 24_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kMode24Frac =
+            ftl::as_non_null(createDisplayMode(kModeId24Frac, 23.976_Hz));
 
     // Test configurations.
     static inline const DisplayModes kModes_60 = makeModes(kMode60);
@@ -298,7 +315,7 @@
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     const auto minRate90 = selector.getMinRefreshRateByPolicy();
     const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -322,7 +339,7 @@
 
     EXPECT_EQ(SetPolicyResult::Changed,
               selector.setDisplayManagerPolicy({kModeId90, {60_Hz, 90_Hz}}));
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     const auto minRate90 = selector.getMinRefreshRateByPolicy();
     const auto performanceRate90 = selector.getMaxRefreshRateByPolicy();
@@ -358,7 +375,7 @@
         EXPECT_EQ(mode.getId(), kModeId60);
     }
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     {
         const auto& mode = selector.getActiveMode();
         EXPECT_EQ(mode.getId(), kModeId90);
@@ -1805,7 +1822,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Verify that we won't do a seamless switch if we request the same mode as the default
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
@@ -1826,7 +1843,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Verify that if the active mode is in another group and there are no layers with
     // Seamlessness::SeamedAndSeamless, we should switch back to the default group.
@@ -1850,7 +1867,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // If there's a layer with Seamlessness::SeamedAndSeamless, another layer with
     // Seamlessness::OnlySeamless can't change the mode group.
@@ -1879,7 +1896,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // If there's a focused layer with Seamlessness::SeamedAndSeamless, another layer with
     // Seamlessness::Default can't change the mode group back to the group of the default
@@ -1912,7 +1929,7 @@
     policy.allowGroupSwitching = true;
     EXPECT_EQ(SetPolicyResult::Changed, selector.setPolicy(policy));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     // Layer with Seamlessness::Default can change the mode group if there's an
     // unfocused layer with Seamlessness::SeamedAndSeamless. For example, this happens
@@ -1953,7 +1970,7 @@
 
     EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId());
 
-    selector.setActiveModeId(kModeId120);
+    selector.setActiveMode(kModeId120, 120_Hz);
     EXPECT_EQ(kModeId120, selector.getBestFrameRateMode(layers)->getId());
 }
 
@@ -1984,7 +2001,7 @@
     auto& seamedLayer = layers[0];
     seamedLayer.desiredRefreshRate = 30_Hz;
     seamedLayer.name = "30Hz ExplicitDefault";
-    selector.setActiveModeId(kModeId30);
+    selector.setActiveMode(kModeId30, 30_Hz);
 
     EXPECT_EQ(kModeId25, selector.getBestFrameRateMode(layers)->getId());
 }
@@ -2102,7 +2119,7 @@
     EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
 
     {
         constexpr bool kTouchActive = false;
@@ -2142,7 +2159,7 @@
 
     struct Expectation {
         Fps fps;
-        DisplayModePtr mode;
+        ftl::NonNull<DisplayModePtr> mode;
     };
 
     const std::initializer_list<Expectation> knownFrameRatesExpectations = {
@@ -2185,33 +2202,39 @@
     explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
 
     if (GetParam() == Config::FrameRateOverride::Disabled) {
-        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz, selector.getBestScoredFrameRate(layers));
-        EXPECT_SCORED_FRAME_RATE(kMode30, 30_Hz,
-                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
 
     } else {
-        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
-        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz,
-                                 selector.getBestScoredFrameRate(layers, {.touch = true}));
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers, {.touch = true})
+                                       .frameRateMode);
     }
 
     explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
     explicitExactLayer.desiredRefreshRate = 60_Hz;
 
     if (GetParam() == Config::FrameRateOverride::Disabled) {
-        EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
     } else {
-        EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+        EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+                               selector.getBestScoredFrameRate(layers).frameRateMode);
     }
 
     explicitExactLayer.desiredRefreshRate = 72_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode72, 72_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 
     explicitExactLayer.desiredRefreshRate = 90_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 
     explicitExactLayer.desiredRefreshRate = 120_Hz;
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ReadsCache) {
@@ -2295,6 +2318,10 @@
 
 // b/190578904
 TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withCloseRefreshRates) {
+    if (g_noSlowTests) {
+        GTEST_SKIP();
+    }
+
     const int kMinRefreshRate = RefreshRateSelector::kMinSupportedFrameRate.getIntValue();
     constexpr int kMaxRefreshRate = 240;
 
@@ -2409,23 +2436,23 @@
     Fps displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(1, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId60);
+    selector.setActiveMode(kModeId60, 60_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(2, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId72);
+    selector.setActiveMode(kModeId72, 72_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(0, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(3, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId120);
+    selector.setActiveMode(kModeId120, 120_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, frameRate));
 
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     displayRefreshRate = selector.getActiveMode().getFps();
     EXPECT_EQ(4, RefreshRateSelector::getFrameRateDivisor(displayRefreshRate, 22.5_Hz));
 
@@ -2808,7 +2835,7 @@
     constexpr FpsRanges kAppRequest = {/*physical*/ k0_120Hz,
                                        /*render*/ k0_120Hz};
 
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
                                         /*render*/ k0_120Hz};
@@ -2819,7 +2846,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode120, 120_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, selector.getBestScoredFrameRate().frameRateMode);
 
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_60Hz,
@@ -2831,7 +2858,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
 
     {
         constexpr FpsRanges kPrimary = {/*physical*/ k0_120Hz,
@@ -2843,7 +2870,7 @@
                                                     /*appRequestRanges*/
                                                     kAppRequest}));
     }
-    EXPECT_SCORED_FRAME_RATE(kMode60, 60_Hz, selector.getBestScoredFrameRate());
+    EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate().frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, renderFrameRates_60_120) {
@@ -2858,17 +2885,20 @@
     layer.name = "30Hz ExplicitDefault";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::ExplicitDefault;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 
     layer.name = "30Hz Heuristic";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::Heuristic;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 
     layer.name = "30Hz ExplicitExactOrMultiple";
     layer.desiredRefreshRate = 30_Hz;
     layer.vote = LayerVoteType::ExplicitExactOrMultiple;
-    EXPECT_SCORED_FRAME_RATE(kMode60, expectedRenderRate, selector.getBestScoredFrameRate(layers));
+    EXPECT_FRAME_RATE_MODE(kMode60, expectedRenderRate,
+                           selector.getBestScoredFrameRate(layers).frameRateMode);
 }
 
 TEST_P(RefreshRateSelectorTest, idleWhenLowestRefreshRateIsNotDivisor) {
@@ -2897,7 +2927,7 @@
     EXPECT_EQ(kModeId90, selector.getBestFrameRateMode({}, {.touch = true, .idle = true})->getId());
 
     // Idle should be higher precedence than other layer frame rate considerations.
-    selector.setActiveModeId(kModeId90);
+    selector.setActiveMode(kModeId90, 90_Hz);
     {
         constexpr bool kTouchActive = false;
         EXPECT_EQ(kModeId35, getIdleDisplayModeId(LayerVoteType::NoVote, kTouchActive));
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 8e333a3..3ee53c9 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -58,22 +58,22 @@
     SchedulerTest();
 
     static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
-    static inline const DisplayModePtr kDisplay1Mode60 =
-            createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kDisplay1Mode120 =
-            createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
     static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
 
     static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
-    static inline const DisplayModePtr kDisplay2Mode60 =
-            createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz);
-    static inline const DisplayModePtr kDisplay2Mode120 =
-            createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode120 =
+            ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(1), 120_Hz));
     static inline const DisplayModes kDisplay2Modes = makeModes(kDisplay2Mode60, kDisplay2Mode120);
 
     static constexpr PhysicalDisplayId kDisplayId3 = PhysicalDisplayId::fromPort(253u);
-    static inline const DisplayModePtr kDisplay3Mode60 =
-            createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz);
+    static inline const ftl::NonNull<DisplayModePtr> kDisplay3Mode60 =
+            ftl::as_non_null(createDisplayMode(kDisplayId3, DisplayModeId(0), 60_Hz));
     static inline const DisplayModes kDisplay3Modes = makeModes(kDisplay3Mode60);
 
     std::shared_ptr<RefreshRateSelector> mSelector =
@@ -217,7 +217,9 @@
     // If the handle is incorrect, the function should return before
     // onModeChange is called.
     ConnectionHandle invalidHandle = {.id = 123};
-    EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
+    EXPECT_NO_FATAL_FAILURE(
+            mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle,
+                                                       {90_Hz, ftl::as_non_null(mode)}));
     EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
 }
 
@@ -232,7 +234,7 @@
 }
 
 MATCHER(Is120Hz, "") {
-    return isApproxEqual(arg.front().modePtr->getFps(), 120_Hz);
+    return isApproxEqual(arg.front().mode.fps, 120_Hz);
 }
 
 TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
@@ -277,7 +279,7 @@
 
     auto choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode60, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, globalSignals));
 
     globalSignals = {.idle = false};
     mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -287,7 +289,7 @@
 
     choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
 
     globalSignals = {.touch = true};
     mScheduler->replaceTouchTimer(10);
@@ -298,7 +300,7 @@
 
     choice = modeChoices.get(kDisplayId1);
     ASSERT_TRUE(choice);
-    EXPECT_EQ(choice->get(), DisplayModeChoice(kDisplay1Mode120, globalSignals));
+    EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
 
     mScheduler->unregisterDisplay(kDisplayId1);
     EXPECT_FALSE(mScheduler->hasRefreshRateSelectors());
@@ -319,8 +321,11 @@
         const GlobalSignals globalSignals = {.idle = true};
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode60,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{60_Hz, kDisplay1Mode60},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay2Mode60},
                                                                  globalSignals);
 
         std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
@@ -335,8 +340,11 @@
         const GlobalSignals globalSignals = {.idle = false};
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode120,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
                                                                  globalSignals);
 
         mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -351,8 +359,11 @@
 
         expectedChoices =
                 ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode120,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode120,
+                               DisplayModeChoice>(kDisplayId1,
+                                                  FrameRateMode{120_Hz, kDisplay1Mode120},
+                                                  globalSignals)(kDisplayId2,
+                                                                 FrameRateMode{120_Hz,
+                                                                               kDisplay2Mode120},
                                                                  globalSignals);
 
         const auto actualChoices = mScheduler->chooseDisplayModes();
@@ -369,13 +380,15 @@
         mScheduler->replaceTouchTimer(10);
         mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
 
-        expectedChoices =
-                ftl::init::map<const PhysicalDisplayId&,
-                               DisplayModeChoice>(kDisplayId1, kDisplay1Mode60,
-                                                  globalSignals)(kDisplayId2, kDisplay2Mode60,
-                                                                 globalSignals)(kDisplayId3,
-                                                                                kDisplay3Mode60,
-                                                                                globalSignals);
+        expectedChoices = ftl::init::map<
+                const PhysicalDisplayId&,
+                DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
+                                   globalSignals)(kDisplayId2,
+                                                  FrameRateMode{60_Hz, kDisplay2Mode60},
+                                                  globalSignals)(kDisplayId3,
+                                                                 FrameRateMode{60_Hz,
+                                                                               kDisplay3Mode60},
+                                                                 globalSignals);
 
         const auto actualChoices = mScheduler->chooseDisplayModes();
         EXPECT_EQ(expectedChoices, actualChoices);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 05d0ebf..b81693a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -116,7 +116,7 @@
     ftl::FakeGuard guard(kMainThreadContext);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -125,8 +125,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -139,7 +139,7 @@
 
     Mock::VerifyAndClearExpectations(mComposer);
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that the next commit will complete the mode change and send
     // a onModeChanged event to the framework.
@@ -149,7 +149,7 @@
     Mock::VerifyAndClearExpectations(mAppEventThread);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
@@ -164,8 +164,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -180,7 +180,7 @@
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
 }
 
 TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -190,7 +190,7 @@
     // is still being processed the later call will be respected.
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -211,7 +211,7 @@
                                                                      180));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     EXPECT_CALL(*mComposer,
                 setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
@@ -221,19 +221,19 @@
     mFlinger.commit();
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId120);
 
     mFlinger.commit();
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId120);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
 }
 
 TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
     ftl::FakeGuard guard(kMainThreadContext);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
@@ -242,8 +242,8 @@
                                                                      120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId60);
+    ASSERT_EQ(mDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
 
     // Verify that next commit will call setActiveConfigWithConstraints in HWC
     // and complete the mode change.
@@ -278,7 +278,7 @@
     mDisplay = mFlinger.getDisplay(displayToken);
 
     ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
-    ASSERT_EQ(mDisplay->getActiveMode().getId(), kModeId90_4K);
+    ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 0384568..c0796df 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -288,7 +288,7 @@
 
     if constexpr (Case::Display::CONNECTION_TYPE::value) {
         ftl::FakeGuard guard(kMainThreadContext);
-        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().getHwcId());
+        EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f8fe0d..54c10c5 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,8 +44,7 @@
           : Scheduler(*this, callback, Feature::kContentDetection) {
         mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
 
-        const auto displayId = FTL_FAKE_GUARD(kMainThreadContext,
-                                              selectorPtr->getActiveMode().getPhysicalDisplayId());
+        const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr));
 
         ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
@@ -147,7 +146,7 @@
         mPolicy.cachedModeChangedParams.reset();
     }
 
-    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
+    void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, const FrameRateMode& mode) {
         Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
     }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c15b3c8..e29dd67 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -221,7 +221,7 @@
             selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
         }
 
-        const auto fps = FTL_FAKE_GUARD(kMainThreadContext, selectorPtr->getActiveMode().getFps());
+        const auto fps = selectorPtr->getActiveMode().fps;
         mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
         mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
                 mFlinger->mVsyncConfiguration->getCurrentConfigs());
@@ -857,23 +857,24 @@
 
                 const auto activeMode = modes.get(activeModeId);
                 LOG_ALWAYS_FATAL_IF(!activeMode);
+                const auto fps = activeMode->get()->getFps();
 
                 state.physical = {.id = physicalId,
                                   .hwcDisplayId = *mHwcDisplayId,
                                   .activeMode = activeMode->get()};
 
-                const auto it = mFlinger.mutablePhysicalDisplays()
-                                        .emplace_or_replace(physicalId, mDisplayToken, physicalId,
-                                                            *mConnectionType, std::move(modes),
-                                                            ui::ColorModes(), std::nullopt)
-                                        .first;
+                mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
+                                                                      physicalId, *mConnectionType,
+                                                                      std::move(modes),
+                                                                      ui::ColorModes(),
+                                                                      std::nullopt);
 
                 if (mFlinger.scheduler()) {
                     mFlinger.scheduler()->registerDisplay(physicalId,
                                                           display->holdRefreshRateSelector());
                 }
 
-                display->setActiveMode(activeModeId, it->second.snapshot());
+                display->setActiveMode(activeModeId, fps, fps);
             }
 
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 2da266b..47c2dee 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setDivisor(unsigned) final {}
     void dump(std::string&) const final {}
 
 private:
@@ -91,6 +92,7 @@
     void resetModel() final {}
     bool needsMoreSamples() const final { return false; }
     bool isVSyncInPhase(nsecs_t, Fps) const final { return false; }
+    void setDivisor(unsigned) final {}
     void dump(std::string&) const final {}
 
 private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index f660753..2b86e94 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -55,6 +55,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 74d2b7d..3095e8a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -532,6 +532,26 @@
     EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
 }
 
+TEST_F(VSyncPredictorTest, setDivisorIsRespected) {
+    auto last = mNow;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+    }
+
+    tracker.setDivisor(3);
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index a35ff96..8bd689a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -50,6 +50,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
new file mode 100644
index 0000000..ef9cd9b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <scheduler/FrameRateMode.h>
+
+// Use a C style macro to keep the line numbers printed in gtest
+#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
+    EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 5b0c1f3..6893154 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -34,6 +34,7 @@
     MOCK_METHOD0(resetModel, void());
     MOCK_CONST_METHOD0(needsMoreSamples, bool());
     MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
+    MOCK_METHOD(void, setDivisor, (unsigned), (override));
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };