SF: store the render frame rate in RefreshRateConfigs::Policy

Add plumbing for the render frame rate passed from DM and update the
policy accordingly.

Test: SF unit tests
Bug: 241460058
Change-Id: I86088001d6d6e5302516f42aa5c9ede4a918dae1
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 40af6ee..f22f9e7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -164,9 +164,10 @@
 
 std::string RefreshRateSelector::Policy::toString() const {
     return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
-                              ", primaryRange=%s, appRequestRange=%s}",
+                              ", primaryRanges=%s, appRequestRanges=%s}",
                               defaultMode.value(), allowGroupSwitching ? "true" : "false",
-                              to_string(primaryRange).c_str(), to_string(appRequestRange).c_str());
+                              to_string(primaryRanges).c_str(),
+                              to_string(appRequestRanges).c_str());
 }
 
 std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod,
@@ -381,7 +382,7 @@
     // move out the of range if layers explicitly request a different refresh
     // rate.
     const bool primaryRangeIsSingleRate =
-            isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
+            isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max);
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle");
@@ -450,7 +451,7 @@
                 continue;
             }
 
-            const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps());
+            const bool inPrimaryRange = policy->primaryRanges.physical.includes(mode->getFps());
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
@@ -902,7 +903,7 @@
 bool RefreshRateSelector::isPolicyValidLocked(const Policy& policy) const {
     // defaultMode must be a valid mode, and within the given refresh rate range.
     if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
-        if (!policy.primaryRange.includes(mode->get()->getFps())) {
+        if (!policy.primaryRanges.physical.includes(mode->get()->getFps())) {
             ALOGE("Default mode is not in the primary range.");
             return false;
         }
@@ -912,8 +913,8 @@
     }
 
     using namespace fps_approx_ops;
-    return policy.appRequestRange.min <= policy.primaryRange.min &&
-            policy.appRequestRange.max >= policy.primaryRange.max;
+    return policy.appRequestRanges.physical.min <= policy.primaryRanges.physical.min &&
+            policy.appRequestRanges.physical.max >= policy.primaryRanges.physical.max;
 }
 
 auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyResult {
@@ -1026,8 +1027,8 @@
         return modes;
     };
 
-    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary");
-    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
+    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRanges.physical, "primary");
+    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRanges.physical, "app request");
 }
 
 Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
@@ -1067,7 +1068,7 @@
     if (minByPolicy == maxByPolicy) {
         // Turn on the timer when the min of the primary range is below the device min.
         if (const Policy* currentPolicy = getCurrentPolicyLocked();
-            isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) {
+            isApproxLess(currentPolicy->primaryRanges.physical.min, deviceMinFps)) {
             return KernelIdleTimerAction::TurnOn;
         }
         return KernelIdleTimerAction::TurnOff;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index bff16d3..887d815 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -67,40 +67,31 @@
         DisplayModeId defaultMode;
         // Whether or not we switch mode groups to get the best frame rate.
         bool allowGroupSwitching = kAllowGroupSwitchingDefault;
-        // The primary refresh rate range represents display manager's general guidance on the
-        // display modes we'll consider when switching refresh rates. Unless we get an explicit
-        // signal from an app, we should stay within this range.
-        FpsRange primaryRange;
-        // The app request refresh rate range allows us to consider more display modes when
-        // switching refresh rates. Although we should generally stay within the primary range,
-        // specific considerations, such as layer frame rate settings specified via the
-        // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
-        // app request range. The app request range will be greater than or equal to the primary
-        // refresh rate range, never smaller.
-        FpsRange appRequestRange;
+        // The primary refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+        // TODO(b/257072060): use the render range when selecting SF render rate
+        //  or the app override frame rate
+        FpsRanges primaryRanges;
+        // The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details.
+        FpsRanges appRequestRanges;
 
         Policy() = default;
 
-        Policy(DisplayModeId defaultMode, FpsRange range)
-              : Policy(defaultMode, kAllowGroupSwitchingDefault, range, range) {}
+        Policy(DisplayModeId defaultMode, FpsRange range,
+               bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+              : Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range},
+                       allowGroupSwitching) {}
 
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange range)
-              : Policy(defaultMode, allowGroupSwitching, range, range) {}
-
-        Policy(DisplayModeId defaultMode, FpsRange primaryRange, FpsRange appRequestRange)
-              : Policy(defaultMode, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
-
-        Policy(DisplayModeId defaultMode, bool allowGroupSwitching, FpsRange primaryRange,
-               FpsRange appRequestRange)
+        Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges,
+               bool allowGroupSwitching = kAllowGroupSwitchingDefault)
               : defaultMode(defaultMode),
                 allowGroupSwitching(allowGroupSwitching),
-                primaryRange(primaryRange),
-                appRequestRange(appRequestRange) {}
+                primaryRanges(primaryRanges),
+                appRequestRanges(appRequestRanges) {}
 
         bool operator==(const Policy& other) const {
             using namespace fps_approx_ops;
-            return defaultMode == other.defaultMode && primaryRange == other.primaryRange &&
-                    appRequestRange == other.appRequestRange &&
+            return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges &&
+                    appRequestRanges == other.appRequestRanges &&
                     allowGroupSwitching == other.allowGroupSwitching;
         }
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index bd4f409..d89f685 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -68,6 +68,15 @@
     bool includes(Fps) const;
 };
 
+struct FpsRanges {
+    // The range of refresh rates that refers to the display mode setting.
+    FpsRange physical;
+
+    // the range of frame rates that refers to the render rate, which is
+    // the rate that frames are swapped.
+    FpsRange render;
+};
+
 static_assert(std::is_trivially_copyable_v<Fps>);
 
 constexpr Fps operator""_Hz(unsigned long long frequency) {
@@ -127,6 +136,14 @@
     return !(lhs == rhs);
 }
 
+inline bool operator==(const FpsRanges& lhs, const FpsRanges& rhs) {
+    return lhs.physical == rhs.physical && lhs.render == rhs.render;
+}
+
+inline bool operator!=(const FpsRanges& lhs, const FpsRanges& rhs) {
+    return !(lhs == rhs);
+}
+
 } // namespace fps_approx_ops
 
 inline bool FpsRange::includes(Fps fps) const {
@@ -151,4 +168,10 @@
     return base::StringPrintf("[%s, %s]", to_string(min).c_str(), to_string(max).c_str());
 }
 
+inline std::string to_string(FpsRanges ranges) {
+    const auto& [physical, render] = ranges;
+    return base::StringPrintf("{physical=%s, render=%s}", to_string(physical).c_str(),
+                              to_string(render).c_str());
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 89d905a..2ca39d0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1132,8 +1132,8 @@
                 display->refreshRateSelector().getCurrentPolicy().allowGroupSwitching;
 
         const scheduler::RefreshRateSelector::DisplayManagerPolicy policy{modeId,
-                                                                          allowGroupSwitching,
-                                                                          {fps, fps}};
+                                                                          {fps, fps},
+                                                                          allowGroupSwitching};
 
         return setDesiredDisplayModeSpecsInternal(display, policy);
     });
@@ -6651,10 +6651,33 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDesiredDisplayModeSpecs(
-        const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching,
-        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
-        float appRequestRefreshRateMax) {
+namespace {
+FpsRange translate(const gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange& aidlRange) {
+    return FpsRange{Fps::fromValue(aidlRange.min), Fps::fromValue(aidlRange.max)};
+}
+
+FpsRanges translate(const gui::DisplayModeSpecs::RefreshRateRanges& aidlRanges) {
+    return FpsRanges{translate(aidlRanges.physical), translate(aidlRanges.render)};
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange translate(const FpsRange& range) {
+    gui::DisplayModeSpecs::RefreshRateRanges::RefreshRateRange aidlRange;
+    aidlRange.min = range.min.getValue();
+    aidlRange.max = range.max.getValue();
+    return aidlRange;
+}
+
+gui::DisplayModeSpecs::RefreshRateRanges translate(const FpsRanges& ranges) {
+    gui::DisplayModeSpecs::RefreshRateRanges aidlRanges;
+    aidlRanges.physical = translate(ranges.physical);
+    aidlRanges.render = translate(ranges.render);
+    return aidlRanges;
+}
+
+} // namespace
+
+status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                    const gui::DisplayModeSpecs& specs) {
     ATRACE_CALL();
 
     if (!displayToken) {
@@ -6672,12 +6695,8 @@
             return INVALID_OPERATION;
         } else {
             using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy;
-            const Policy policy{DisplayModeId(defaultMode),
-                                allowGroupSwitching,
-                                {Fps::fromValue(primaryRefreshRateMin),
-                                 Fps::fromValue(primaryRefreshRateMax)},
-                                {Fps::fromValue(appRequestRefreshRateMin),
-                                 Fps::fromValue(appRequestRefreshRateMax)}};
+            const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges),
+                                translate(specs.appRequestRanges), specs.allowGroupSwitching};
 
             return setDesiredDisplayModeSpecsInternal(display, policy);
         }
@@ -6687,16 +6706,10 @@
 }
 
 status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                                    ui::DisplayModeId* outDefaultMode,
-                                                    bool* outAllowGroupSwitching,
-                                                    float* outPrimaryRefreshRateMin,
-                                                    float* outPrimaryRefreshRateMax,
-                                                    float* outAppRequestRefreshRateMin,
-                                                    float* outAppRequestRefreshRateMax) {
+                                                    gui::DisplayModeSpecs* outSpecs) {
     ATRACE_CALL();
 
-    if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin ||
-        !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+    if (!displayToken || !outSpecs) {
         return BAD_VALUE;
     }
 
@@ -6712,12 +6725,10 @@
 
     scheduler::RefreshRateSelector::Policy policy =
             display->refreshRateSelector().getDisplayManagerPolicy();
-    *outDefaultMode = policy.defaultMode.value();
-    *outAllowGroupSwitching = policy.allowGroupSwitching;
-    *outPrimaryRefreshRateMin = policy.primaryRange.min.getValue();
-    *outPrimaryRefreshRateMax = policy.primaryRange.max.getValue();
-    *outAppRequestRefreshRateMin = policy.appRequestRange.min.getValue();
-    *outAppRequestRefreshRateMax = policy.appRequestRange.max.getValue();
+    outSpecs->defaultMode = policy.defaultMode.value();
+    outSpecs->allowGroupSwitching = policy.allowGroupSwitching;
+    outSpecs->primaryRanges = translate(policy.primaryRanges);
+    outSpecs->appRequestRanges = translate(policy.appRequestRanges);
     return NO_ERROR;
 }
 
@@ -7608,18 +7619,11 @@
     return binderStatusFromStatusT(status);
 }
 
-binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(
-        const sp<IBinder>& displayToken, int32_t defaultMode, bool allowGroupSwitching,
-        float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
-        float appRequestRefreshRateMax) {
+binder::Status SurfaceComposerAIDL::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                                               const gui::DisplayModeSpecs& specs) {
     status_t status = checkAccessPermission();
     if (status == OK) {
-        status = mFlinger->setDesiredDisplayModeSpecs(displayToken,
-                                                      static_cast<ui::DisplayModeId>(defaultMode),
-                                                      allowGroupSwitching, primaryRefreshRateMin,
-                                                      primaryRefreshRateMax,
-                                                      appRequestRefreshRateMin,
-                                                      appRequestRefreshRateMax);
+        status = mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
     }
     return binderStatusFromStatusT(status);
 }
@@ -7635,25 +7639,7 @@
         return binderStatusFromStatusT(status);
     }
 
-    ui::DisplayModeId displayModeId;
-    bool allowGroupSwitching;
-    float primaryRefreshRateMin;
-    float primaryRefreshRateMax;
-    float appRequestRefreshRateMin;
-    float appRequestRefreshRateMax;
-    status = mFlinger->getDesiredDisplayModeSpecs(displayToken, &displayModeId,
-                                                  &allowGroupSwitching, &primaryRefreshRateMin,
-                                                  &primaryRefreshRateMax, &appRequestRefreshRateMin,
-                                                  &appRequestRefreshRateMax);
-    if (status == NO_ERROR) {
-        outSpecs->defaultMode = displayModeId;
-        outSpecs->allowGroupSwitching = allowGroupSwitching;
-        outSpecs->primaryRefreshRateMin = primaryRefreshRateMin;
-        outSpecs->primaryRefreshRateMax = primaryRefreshRateMax;
-        outSpecs->appRequestRefreshRateMin = appRequestRefreshRateMin;
-        outSpecs->appRequestRefreshRateMax = appRequestRefreshRateMax;
-    }
-
+    status = mFlinger->getDesiredDisplayModeSpecs(displayToken, outSpecs);
     return binderStatusFromStatusT(status);
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d290762..c07e19c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -551,17 +551,8 @@
     status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
     status_t removeTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& listener);
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                        ui::DisplayModeId displayModeId, bool allowGroupSwitching,
-                                        float primaryRefreshRateMin, float primaryRefreshRateMax,
-                                        float appRequestRefreshRateMin,
-                                        float appRequestRefreshRateMax);
-    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
-                                        ui::DisplayModeId* outDefaultMode,
-                                        bool* outAllowGroupSwitching,
-                                        float* outPrimaryRefreshRateMin,
-                                        float* outPrimaryRefreshRateMax,
-                                        float* outAppRequestRefreshRateMin,
-                                        float* outAppRequestRefreshRateMax);
+                                        const gui::DisplayModeSpecs&);
+    status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, gui::DisplayModeSpecs*);
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken,
                                   const gui::DisplayBrightness& brightness);
@@ -1451,11 +1442,8 @@
             const sp<gui::ITunnelModeEnabledListener>& listener) override;
     binder::Status removeTunnelModeEnabledListener(
             const sp<gui::ITunnelModeEnabledListener>& listener) override;
-    binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, int32_t defaultMode,
-                                              bool allowGroupSwitching, float primaryRefreshRateMin,
-                                              float primaryRefreshRateMax,
-                                              float appRequestRefreshRateMin,
-                                              float appRequestRefreshRateMax) override;
+    binder::Status setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                              const gui::DisplayModeSpecs&) override;
     binder::Status getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                               gui::DisplayModeSpecs* outSpecs) override;
     binder::Status getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 94de6e5..9ba9b90 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -505,16 +505,8 @@
     }
 
     void getDesiredDisplayModeSpecs(sp<IBinder> &display) {
-        ui::DisplayModeId outDefaultMode;
-        bool outAllowGroupSwitching;
-        float outPrimaryRefreshRateMin;
-        float outPrimaryRefreshRateMax;
-        float outAppRequestRefreshRateMin;
-        float outAppRequestRefreshRateMax;
-        mFlinger->getDesiredDisplayModeSpecs(display, &outDefaultMode, &outAllowGroupSwitching,
-                                             &outPrimaryRefreshRateMin, &outPrimaryRefreshRateMax,
-                                             &outAppRequestRefreshRateMin,
-                                             &outAppRequestRefreshRateMax);
+        gui::DisplayModeSpecs _;
+        mFlinger->getDesiredDisplayModeSpecs(display, &_);
     }
 
     void setVsyncConfig(FuzzedDataProvider *fdp) {
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 4f04934..1676844 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -207,23 +207,12 @@
 
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = getFirstDisplayToken();
-    ui::DisplayModeId defaultMode;
-    bool allowGroupSwitching;
-    float primaryFpsMin;
-    float primaryFpsMax;
-    float appRequestFpsMin;
-    float appRequestFpsMax;
-    status_t res =
-            SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &defaultMode,
-                                                              &allowGroupSwitching, &primaryFpsMin,
-                                                              &primaryFpsMax, &appRequestFpsMin,
-                                                              &appRequestFpsMax);
+    gui::DisplayModeSpecs specs;
+    status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(display, &specs);
     ASSERT_EQ(res, NO_ERROR);
+    gui::DisplayModeSpecs setSpecs;
     std::function<status_t()> condition = [=]() {
-        return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, defaultMode,
-                                                                 allowGroupSwitching, primaryFpsMin,
-                                                                 primaryFpsMax, appRequestFpsMin,
-                                                                 appRequestFpsMax);
+        return SurfaceComposerClient::setDesiredDisplayModeSpecs(display, specs);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 02c934e..10dae46 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -39,37 +39,19 @@
  */
 class RefreshRateRangeTest : public ::testing::Test {
 private:
-    ui::DisplayModeId initialDefaultMode;
-    bool initialAllowGroupSwitching;
-    float initialPrimaryMin;
-    float initialPrimaryMax;
-    float initialAppRequestMin;
-    float initialAppRequestMax;
+    gui::DisplayModeSpecs mSpecs;
 
 protected:
     void SetUp() override {
         const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
         ASSERT_FALSE(ids.empty());
         mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-        status_t res =
-                SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken,
-                                                                  &initialDefaultMode,
-                                                                  &initialAllowGroupSwitching,
-                                                                  &initialPrimaryMin,
-                                                                  &initialPrimaryMax,
-                                                                  &initialAppRequestMin,
-                                                                  &initialAppRequestMax);
+        status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
         ASSERT_EQ(res, NO_ERROR);
     }
 
     void TearDown() override {
-        status_t res =
-                SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, initialDefaultMode,
-                                                                  initialAllowGroupSwitching,
-                                                                  initialPrimaryMin,
-                                                                  initialPrimaryMax,
-                                                                  initialAppRequestMin,
-                                                                  initialAppRequestMax);
+        status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, mSpecs);
         ASSERT_EQ(res, NO_ERROR);
     }
 
@@ -85,61 +67,39 @@
     ASSERT_EQ(res, NO_ERROR);
     ASSERT_GT(modes.size(), 0);
 
+    gui::DisplayModeSpecs setSpecs;
+    setSpecs.allowGroupSwitching = false;
     for (size_t i = 0; i < modes.size(); i++) {
-        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate,
-                                                                modes[i].refreshRate);
+        setSpecs.defaultMode = modes[i].id;
+        setSpecs.primaryRanges.physical.min = modes[i].refreshRate;
+        setSpecs.primaryRanges.physical.max = modes[i].refreshRate;
+        setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+        setSpecs.appRequestRanges = setSpecs.primaryRanges;
+        res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
         ASSERT_EQ(res, NO_ERROR);
 
-        ui::DisplayModeId defaultConfig;
-        bool allowGroupSwitching;
-        float primaryRefreshRateMin;
-        float primaryRefreshRateMax;
-        float appRequestRefreshRateMin;
-        float appRequestRefreshRateMax;
-        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
-                                                                &allowGroupSwitching,
-                                                                &primaryRefreshRateMin,
-                                                                &primaryRefreshRateMax,
-                                                                &appRequestRefreshRateMin,
-                                                                &appRequestRefreshRateMax);
+        gui::DisplayModeSpecs getSpecs;
+        res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
         ASSERT_EQ(res, NO_ERROR);
-        ASSERT_EQ(defaultConfig, i);
-        ASSERT_EQ(allowGroupSwitching, false);
-        ASSERT_EQ(primaryRefreshRateMin, modes[i].refreshRate);
-        ASSERT_EQ(primaryRefreshRateMax, modes[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMin, modes[i].refreshRate);
-        ASSERT_EQ(appRequestRefreshRateMax, modes[i].refreshRate);
+        ASSERT_EQ(setSpecs, getSpecs);
     }
 }
 
 void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
-    status_t res =
-            SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching,
-                                                              0.f, 90.f, 0.f, 90.f);
-    ASSERT_EQ(res, NO_ERROR);
-    ui::DisplayModeId defaultConfig;
-    bool newAllowGroupSwitching;
-    float primaryRefreshRateMin;
-    float primaryRefreshRateMax;
-    float appRequestRefreshRateMin;
-    float appRequestRefreshRateMax;
+    gui::DisplayModeSpecs setSpecs;
+    setSpecs.defaultMode = 0;
+    setSpecs.allowGroupSwitching = allowGroupSwitching;
+    setSpecs.primaryRanges.physical.min = 0;
+    setSpecs.primaryRanges.physical.max = 90;
+    setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical;
+    setSpecs.appRequestRanges = setSpecs.primaryRanges;
 
-    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &defaultConfig,
-                                                            &newAllowGroupSwitching,
-                                                            &primaryRefreshRateMin,
-                                                            &primaryRefreshRateMax,
-                                                            &appRequestRefreshRateMin,
-                                                            &appRequestRefreshRateMax);
+    status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs);
     ASSERT_EQ(res, NO_ERROR);
-    ASSERT_EQ(defaultConfig, 0);
-    ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
-    ASSERT_EQ(primaryRefreshRateMin, 0.f);
-    ASSERT_EQ(primaryRefreshRateMax, 90.f);
-    ASSERT_EQ(appRequestRefreshRateMin, 0.f);
-    ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+    gui::DisplayModeSpecs getSpecs;
+    res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs);
+    ASSERT_EQ(res, NO_ERROR);
+    ASSERT_EQ(setSpecs, getSpecs);
 }
 
 TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index e7ae53c..9689ddb 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -357,7 +357,7 @@
         constexpr bool kAllowGroupSwitching = true;
         EXPECT_EQ(SetPolicyResult::Changed,
                   selector.setDisplayManagerPolicy(
-                          {kModeId90, kAllowGroupSwitching, {0_Hz, 90_Hz}}));
+                          {kModeId90, {0_Hz, 90_Hz}, kAllowGroupSwitching}));
         EXPECT_EQ(kMode90_G1, selector.getBestRefreshRate());
     }
 }
@@ -1105,7 +1105,7 @@
     TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
 
     const auto refreshRates =
             selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt, RefreshRateOrder::Ascending);
@@ -1126,7 +1126,7 @@
     TestableRefreshRateSelector selector(kModes_30_60_90, kModeId72);
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 90_Hz}}));
 
     const auto refreshRates = selector.rankRefreshRates(/*anchorGroupOpt*/ std::nullopt,
                                                         RefreshRateOrder::Descending);
@@ -1351,8 +1351,10 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
     TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
 
+    constexpr FpsRange k90 = {90_Hz, 90_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1373,8 +1375,11 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
     TestableRefreshRateSelector selector(kModes_60_90, kModeId60);
 
+    constexpr FpsRange k60 = {60_Hz, 60_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {k60, k60}, {k60_90, k60_90}}));
 
     std::vector<LayerRequirement> layers = {{.weight = 1.f}};
     auto& lr = layers[0];
@@ -1513,8 +1518,11 @@
        getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
     TestableRefreshRateSelector selector(kModes_60_90, kModeId90);
 
+    constexpr FpsRange k90 = {90_Hz, 90_Hz};
+    constexpr FpsRange k60_90 = {60_Hz, 90_Hz};
+
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
 
     const auto [ranking, signals] = selector.getRankedRefreshRates({}, {});
     EXPECT_EQ(ranking.front().modePtr, kMode90);
@@ -1849,8 +1857,11 @@
         return selector.getBestRefreshRate(layers, {.touch = args.touch})->getId();
     };
 
+    constexpr FpsRange k30_60 = {30_Hz, 60_Hz};
+    constexpr FpsRange k30_90 = {30_Hz, 90_Hz};
+
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {k30_60, k30_60}, {k30_90, k30_90}}));
 
     EXPECT_EQ(kModeId60, selector.getBestRefreshRate()->getId());
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
@@ -1875,7 +1886,7 @@
               getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 60_Hz}}));
 
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
     EXPECT_EQ(kModeId60, getFrameRate(LayerVoteType::Min, 90_Hz));
@@ -1904,7 +1915,7 @@
     };
 
     EXPECT_EQ(SetPolicyResult::Changed,
-              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}));
+              selector.setDisplayManagerPolicy({kModeId60, {60_Hz, 90_Hz}}));
 
     // Idle should be lower priority than touch boost.
     {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 4c25463..05d0ebf 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "mock/MockDisplayModeSpecs.h"
 #include "mock/MockEventThread.h"
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -119,8 +120,9 @@
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
@@ -157,8 +159,9 @@
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        true, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90);
@@ -191,8 +194,9 @@
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
+                                                                     120));
 
     const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
     EXPECT_CALL(*mComposer,
@@ -202,8 +206,9 @@
 
     mFlinger.commit();
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId120.value(),
-                                        false, 0.f, 180.f, 0.f, 180.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId120.value(), false, 0,
+                                                                     180));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId120);
@@ -232,8 +237,9 @@
 
     mFlinger.onActiveDisplayChanged(mDisplay);
 
-    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), kModeId90_4K.value(),
-                                        false, 0.f, 120.f, 0.f, 120.f);
+    mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+                                        mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
+                                                                     120));
 
     ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
     ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kModeId90_4K);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index ff79ce0..8b2f953 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -455,14 +455,9 @@
         return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
     }
 
-    auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
-                                    bool allowGroupSwitching, float primaryRefreshRateMin,
-                                    float primaryRefreshRateMax, float appRequestRefreshRateMin,
-                                    float appRequestRefreshRateMax) {
-        return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
-                                                    primaryRefreshRateMin, primaryRefreshRateMax,
-                                                    appRequestRefreshRateMin,
-                                                    appRequestRefreshRateMax);
+    auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
+                                    const gui::DisplayModeSpecs& specs) {
+        return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs);
     }
 
     void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
new file mode 100644
index 0000000..a71e82c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/gui/DisplayModeSpecs.h>
+
+namespace android::mock {
+
+inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching,
+                                                    float minFps, float maxFps) {
+    gui::DisplayModeSpecs specs;
+    specs.defaultMode = defaultMode;
+    specs.allowGroupSwitching = allowGroupSwitching;
+    specs.primaryRanges.physical.min = minFps;
+    specs.primaryRanges.physical.max = maxFps;
+    specs.primaryRanges.render = specs.primaryRanges.physical;
+    specs.appRequestRanges = specs.primaryRanges;
+    return specs;
+}
+
+} // namespace android::mock