SF: Unify data types for display modes

Remove the RefreshRateConfigs::RefreshRate wrapper around DisplayMode.
Store DisplayModes as a SmallMap, so that RefreshRateConfigs uses the
same data structure for lookup by ID. Use iterators into that map for
all bookkeeping in RefreshRateConfigs.

Bug: 182939859
Bug: 185535769
Test: libsurfaceflinger_unittest
Change-Id: I7708fa997089802c45d906b17b7a073f5c82105e
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 65c8613..3226f22 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -38,10 +38,33 @@
 namespace android::scheduler {
 namespace {
 
+struct RefreshRateScore {
+    DisplayModeIterator modeIt;
+    float score;
+};
+
+template <typename Iterator>
+const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) {
+    const auto it =
+            std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) {
+                const auto& [modeIt, score] = current;
+
+                std::string name = to_string(modeIt->second->getFps());
+                ALOGV("%s scores %.2f", name.c_str(), score);
+
+                ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100)));
+
+                constexpr float kEpsilon = 0.0001f;
+                return score > max.score * (1 + kEpsilon);
+            });
+
+    return it->modeIt->second;
+}
+
 constexpr RefreshRateConfigs::GlobalSignals kNoSignals;
 
 std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
-    return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
+    return base::StringPrintf("%s (type=%s, weight=%.2f, seamlessness=%s) %s", layer.name.c_str(),
                               ftl::enum_string(layer.vote).c_str(), weight,
                               ftl::enum_string(layer.seamlessness).c_str(),
                               to_string(layer.desiredRefreshRate).c_str());
@@ -52,8 +75,8 @@
     knownFrameRates.reserve(knownFrameRates.size() + modes.size());
 
     // Add all supported refresh rates.
-    for (const auto& mode : modes) {
-        knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
+    for (const auto& [id, mode] : modes) {
+        knownFrameRates.push_back(mode->getFps());
     }
 
     // Sort and remove duplicates.
@@ -64,17 +87,51 @@
     return knownFrameRates;
 }
 
-} // namespace
+// The Filter is a `bool(const DisplayMode&)` predicate.
+template <typename Filter>
+std::vector<DisplayModeIterator> sortByRefreshRate(const DisplayModes& modes, Filter&& filter) {
+    std::vector<DisplayModeIterator> sortedModes;
+    sortedModes.reserve(modes.size());
 
-using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
-using RefreshRate = RefreshRateConfigs::RefreshRate;
+    for (auto it = modes.begin(); it != modes.end(); ++it) {
+        const auto& [id, mode] = *it;
 
-std::string RefreshRate::toString() const {
-    return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
-                              getModeId().value(), mode->getHwcId(), getFps().getValue(),
-                              mode->getWidth(), mode->getHeight(), getModeGroup());
+        if (filter(*mode)) {
+            ALOGV("%s: including mode %d", __func__, id.value());
+            sortedModes.push_back(it);
+        }
+    }
+
+    std::sort(sortedModes.begin(), sortedModes.end(), [](auto it1, auto it2) {
+        const auto& mode1 = it1->second;
+        const auto& mode2 = it2->second;
+
+        if (mode1->getVsyncPeriod() == mode2->getVsyncPeriod()) {
+            return mode1->getGroup() > mode2->getGroup();
+        }
+
+        return mode1->getVsyncPeriod() > mode2->getVsyncPeriod();
+    });
+
+    return sortedModes;
 }
 
+bool canModesSupportFrameRateOverride(const std::vector<DisplayModeIterator>& sortedModes) {
+    for (const auto it1 : sortedModes) {
+        const auto& mode1 = it1->second;
+        for (const auto it2 : sortedModes) {
+            const auto& mode2 = it2->second;
+
+            if (RefreshRateConfigs::getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+} // namespace
+
 std::string RefreshRateConfigs::Policy::toString() const {
     return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
                               ", primary range: %s, app request range: %s",
@@ -94,15 +151,14 @@
     return {quotient, remainder};
 }
 
-bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
-                                       const RefreshRate& refreshRate) const {
+bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const {
     using namespace fps_approx_ops;
 
     switch (layer.vote) {
         case LayerVoteType::ExplicitExactOrMultiple:
         case LayerVoteType::Heuristic:
             if (mConfig.frameRateMultipleThreshold != 0 &&
-                refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
+                refreshRate >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
                 layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
                 // Don't vote high refresh rates past the threshold for layers with a low desired
                 // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
@@ -120,11 +176,11 @@
     return true;
 }
 
-float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
-        const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
+                                                                    Fps refreshRate) const {
     constexpr float kScoreForFractionalPairs = .8f;
 
-    const auto displayPeriod = refreshRate.getVsyncPeriod();
+    const auto displayPeriod = refreshRate.getPeriodNsecs();
     const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
     if (layer.vote == LayerVoteType::ExplicitDefault) {
         // Find the actual rate the layer will render, assuming
@@ -147,7 +203,7 @@
 
     if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
         layer.vote == LayerVoteType::Heuristic) {
-        if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+        if (isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) {
             return kScoreForFractionalPairs;
         }
 
@@ -182,8 +238,7 @@
     return 0;
 }
 
-float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
-                                                    const RefreshRate& refreshRate,
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate,
                                                     bool isSeamlessSwitch) const {
     if (!isVoteAllowed(layer, refreshRate)) {
         return 0;
@@ -195,14 +250,14 @@
 
     // If the layer wants Max, give higher score to the higher refresh rate
     if (layer.vote == LayerVoteType::Max) {
-        const auto ratio = refreshRate.getFps().getValue() /
-                mAppRequestRefreshRates.back()->getFps().getValue();
+        const auto& maxRefreshRate = mAppRequestRefreshRates.back()->second;
+        const auto ratio = refreshRate.getValue() / maxRefreshRate->getFps().getValue();
         // use ratio^2 to get a lower score the more we get further from peak
         return ratio * ratio;
     }
 
     if (layer.vote == LayerVoteType::ExplicitExact) {
-        const int divisor = getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate);
+        const int divisor = getFrameRateDivisor(refreshRate, layer.desiredRefreshRate);
         if (mSupportsFrameRateOverrideByContent) {
             // Since we support frame rate override, allow refresh rates which are
             // multiples of the layer's request, as those apps would be throttled
@@ -215,7 +270,7 @@
 
     // If the layer frame rate is a divisor of the refresh rate it should score
     // the highest score.
-    if (getFrameRateDivisor(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+    if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
         return 1.0f * seamlessness;
     }
 
@@ -227,14 +282,9 @@
             kNonExactMatchingPenalty;
 }
 
-struct RefreshRateScore {
-    const RefreshRate* refreshRate;
-    float score;
-};
-
 auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                             GlobalSignals signals) const
-        -> std::pair<RefreshRate, GlobalSignals> {
+        -> std::pair<DisplayModePtr, GlobalSignals> {
     std::lock_guard lock(mLock);
 
     if (mGetBestRefreshRateCache &&
@@ -249,7 +299,7 @@
 
 auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
                                                   GlobalSignals signals) const
-        -> std::pair<RefreshRate, GlobalSignals> {
+        -> std::pair<DisplayModePtr, GlobalSignals> {
     ATRACE_CALL();
     ALOGV("%s: %zu layers", __func__, layers.size());
 
@@ -298,21 +348,22 @@
             explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
 
     const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
     // If the default mode group is different from the group of current mode,
     // this means a layer requesting a seamed mode switch just disappeared and
     // we should switch back to the default group.
     // However if a seamed layer is still present we anchor around the group
     // of the current mode, in order to prevent unnecessary seamed mode switches
     // (e.g. when pausing a video playback).
-    const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
-                                                     : defaultMode->getModeGroup();
+    const auto anchorGroup =
+            seamedFocusedLayers > 0 ? mActiveModeIt->second->getGroup() : defaultMode->getGroup();
 
     // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
-        ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
-        return {getMaxRefreshRateByPolicyLocked(anchorGroup), GlobalSignals{.touch = true}};
+        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+        ALOGV("TouchBoost - choose %s", to_string(max->getFps()).c_str());
+        return {max, GlobalSignals{.touch = true}};
     }
 
     // If the primary range consists of a single refresh rate then we can only
@@ -322,28 +373,30 @@
             isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
-        ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        return {getMinRefreshRateByPolicyLocked(), GlobalSignals{.idle = true}};
+        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
+        ALOGV("Idle - choose %s", to_string(min->getFps()).c_str());
+        return {min, GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
-        const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-        ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
-        return {refreshRate, kNoSignals};
+        const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+        ALOGV("no layers with votes - choose %s", to_string(max->getFps()).c_str());
+        return {max, kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
-        ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
-        return {getMinRefreshRateByPolicyLocked(), kNoSignals};
+        const DisplayModePtr& min = getMinRefreshRateByPolicyLocked();
+        ALOGV("all layers Min - choose %s", to_string(min->getFps()).c_str());
+        return {min, kNoSignals};
     }
 
     // Find the best refresh rate based on score
     std::vector<RefreshRateScore> scores;
     scores.reserve(mAppRequestRefreshRates.size());
 
-    for (const auto refreshRate : mAppRequestRefreshRates) {
-        scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
+    for (const DisplayModeIterator modeIt : mAppRequestRefreshRates) {
+        scores.emplace_back(RefreshRateScore{modeIt, 0.0f});
     }
 
     for (const auto& layer : layers) {
@@ -354,17 +407,16 @@
             continue;
         }
 
-        auto weight = layer.weight;
+        const auto weight = layer.weight;
 
-        for (auto i = 0u; i < scores.size(); i++) {
-            const bool isSeamlessSwitch =
-                    scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup();
+        for (auto& [modeIt, score] : scores) {
+            const auto& [id, mode] = *modeIt;
+            const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup();
 
             if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
                 ALOGV("%s ignores %s to avoid non-seamless switch. Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
@@ -372,9 +424,8 @@
                 !layer.focused) {
                 ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
                       " Current mode = %s",
-                      formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      formatLayerInfo(layer, weight).c_str(), to_string(*mode).c_str(),
+                      to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
@@ -383,17 +434,14 @@
             // mode group otherwise. In second case, if the current mode group is different
             // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
             // disappeared.
-            const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
+            const bool isInPolicyForDefault = mode->getGroup() == anchorGroup;
             if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
                 ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
-                      scores[i].refreshRate->toString().c_str(),
-                      mCurrentRefreshRate->toString().c_str());
+                      to_string(*mode).c_str(), to_string(*mActiveModeIt->second).c_str());
                 continue;
             }
 
-            const bool inPrimaryRange =
-                    policy->primaryRange.includes(scores[i].refreshRate->getFps());
-
+            const bool inPrimaryRange = policy->primaryRange.includes(mode->getFps());
             if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
                 !(layer.focused &&
                   (layer.vote == LayerVoteType::ExplicitDefault ||
@@ -404,30 +452,31 @@
             }
 
             const auto layerScore =
-                    calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+                    calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch);
             ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
-                  scores[i].refreshRate->getName().c_str(), layerScore);
-            scores[i].score += weight * layerScore;
+                  to_string(mode->getFps()).c_str(), layerScore);
+
+            score += weight * layerScore;
         }
     }
 
     // Now that we scored all the refresh rates we need to pick the one that got the highest score.
     // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
     // or the lower otherwise.
-    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
-            ? getBestRefreshRate(scores.rbegin(), scores.rend())
-            : getBestRefreshRate(scores.begin(), scores.end());
+    const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0
+            ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend())
+            : getMaxScoreRefreshRate(scores.begin(), scores.end());
 
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
         if (std::all_of(scores.begin(), scores.end(),
                         [](RefreshRateScore score) { return score.score == 0; })) {
-            const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
-            ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
-            return {refreshRate, kNoSignals};
+            const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup);
+            ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str());
+            return {max, kNoSignals};
         } else {
-            return {*bestRefreshRate, kNoSignals};
+            return {bestRefreshRate, kNoSignals};
         }
     }
 
@@ -435,7 +484,7 @@
     // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
     // vote we should not change it if we get a touch event. Only apply touch boost if it will
     // actually increase the refresh rate over the normal selection.
-    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+    const DisplayModePtr& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
 
     const bool touchBoostForExplicitExact = [&] {
         if (mSupportsFrameRateOverrideByContent) {
@@ -450,12 +499,12 @@
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
-        bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
-        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
+        bestRefreshRate->getFps() < touchRefreshRate->getFps()) {
+        ALOGV("TouchBoost - choose %s", to_string(touchRefreshRate->getFps()).c_str());
         return {touchRefreshRate, GlobalSignals{.touch = true}};
     }
 
-    return {*bestRefreshRate, kNoSignals};
+    return {bestRefreshRate, kNoSignals};
 }
 
 std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
@@ -489,26 +538,28 @@
     return layersByUid;
 }
 
-std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
-        const AllRefreshRatesMapType& refreshRates) {
-    std::vector<RefreshRateScore> scores;
-    scores.reserve(refreshRates.size());
-    for (const auto& [ignored, refreshRate] : refreshRates) {
-        scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
-    }
-    std::sort(scores.begin(), scores.end(),
-              [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
-    return scores;
-}
-
 RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
-        const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
+        const std::vector<LayerRequirement>& layers, Fps displayRefreshRate,
         GlobalSignals globalSignals) const {
     ATRACE_CALL();
 
-    ALOGV("getFrameRateOverrides %zu layers", layers.size());
+    ALOGV("%s: %zu layers", __func__, layers.size());
+
     std::lock_guard lock(mLock);
-    std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+
+    std::vector<RefreshRateScore> scores;
+    scores.reserve(mDisplayModes.size());
+
+    for (auto it = mDisplayModes.begin(); it != mDisplayModes.end(); ++it) {
+        scores.emplace_back(RefreshRateScore{it, 0.0f});
+    }
+
+    std::sort(scores.begin(), scores.end(), [](const auto& lhs, const auto& rhs) {
+        const auto& mode1 = lhs.modeIt->second;
+        const auto& mode2 = rhs.modeIt->second;
+        return isStrictlyLess(mode1->getFps(), mode2->getFps());
+    });
+
     std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
             groupLayersByUid(layers);
     UidToFrameRateOverride frameRateOverrides;
@@ -524,8 +575,8 @@
             continue;
         }
 
-        for (auto& score : scores) {
-            score.score = 0;
+        for (auto& [_, score] : scores) {
+            score = 0;
         }
 
         for (const auto& layer : layersWithSameUid) {
@@ -536,137 +587,114 @@
             LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
                                 layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
                                 layer->vote != LayerVoteType::ExplicitExact);
-            for (RefreshRateScore& score : scores) {
-                const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
-                                                                  /*isSeamlessSwitch*/ true);
-                score.score += layer->weight * layerScore;
+            for (auto& [modeIt, score] : scores) {
+                constexpr bool isSeamlessSwitch = true;
+                const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(),
+                                                                  isSeamlessSwitch);
+                score += layer->weight * layerScore;
             }
         }
 
         // We just care about the refresh rates which are a divisor of the
         // display refresh rate
-        auto iter =
-                std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
-                    return getFrameRateDivisor(displayFrameRate, score.refreshRate->getFps()) == 0;
-                });
-        scores.erase(iter, scores.end());
+        const auto it = std::remove_if(scores.begin(), scores.end(), [&](RefreshRateScore score) {
+            const auto& [id, mode] = *score.modeIt;
+            return getFrameRateDivisor(displayRefreshRate, mode->getFps()) == 0;
+        });
+        scores.erase(it, scores.end());
 
         // If we never scored any layers, we don't have a preferred frame rate
         if (std::all_of(scores.begin(), scores.end(),
-                        [](const RefreshRateScore& score) { return score.score == 0; })) {
+                        [](RefreshRateScore score) { return score.score == 0; })) {
             continue;
         }
 
         // Now that we scored all the refresh rates we need to pick the one that got the highest
         // score.
-        const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+        const DisplayModePtr& bestRefreshRate =
+                getMaxScoreRefreshRate(scores.begin(), scores.end());
+
         frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
     }
 
     return frameRateOverrides;
 }
 
-template <typename Iter>
-const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
-    constexpr auto kEpsilon = 0.0001f;
-    const RefreshRate* bestRefreshRate = begin->refreshRate;
-    float max = begin->score;
-    for (auto i = begin; i != end; ++i) {
-        const auto [refreshRate, score] = *i;
-        ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
-
-        ATRACE_INT(refreshRate->getName().c_str(), static_cast<int>(std::round(score * 100)));
-
-        if (score > max * (1 + kEpsilon)) {
-            max = score;
-            bestRefreshRate = refreshRate;
-        }
-    }
-
-    return bestRefreshRate;
-}
-
 std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged(
-        std::optional<DisplayModeId> desiredActiveConfigId, bool timerExpired) const {
+        std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
     std::lock_guard lock(mLock);
 
-    const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId)
-                                                : *mCurrentRefreshRate;
-    const auto& min = *mMinSupportedRefreshRate;
+    const DisplayModePtr& current = desiredActiveModeId
+            ? mDisplayModes.get(*desiredActiveModeId)->get()
+            : mActiveModeIt->second;
 
-    if (current != min) {
-        const auto& refreshRate = timerExpired ? min : current;
-        return refreshRate.getFps();
+    const DisplayModePtr& min = mMinRefreshRateModeIt->second;
+    if (current == min) {
+        return {};
     }
 
-    return {};
+    const auto& mode = timerExpired ? min : current;
+    return mode->getFps();
 }
 
-const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
-    for (auto refreshRate : mPrimaryRefreshRates) {
-        if (mCurrentRefreshRate->getModeGroup() == refreshRate->getModeGroup()) {
-            return *refreshRate;
+const DisplayModePtr& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    for (const DisplayModeIterator modeIt : mPrimaryRefreshRates) {
+        const auto& mode = modeIt->second;
+        if (mActiveModeIt->second->getGroup() == mode->getGroup()) {
+            return mode;
         }
     }
+
     ALOGE("Can't find min refresh rate by policy with the same mode group"
           " as the current mode %s",
-          mCurrentRefreshRate->toString().c_str());
-    // Defaulting to the lowest refresh rate
-    return *mPrimaryRefreshRates.front();
+          to_string(*mActiveModeIt->second).c_str());
+
+    // Default to the lowest refresh rate.
+    return mPrimaryRefreshRates.front()->second;
 }
 
-RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+DisplayModePtr RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
     return getMaxRefreshRateByPolicyLocked();
 }
 
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
-    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
-        const auto& refreshRate = (**it);
-        if (anchorGroup == refreshRate.getModeGroup()) {
-            return refreshRate;
+const DisplayModePtr& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
+    for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); ++it) {
+        const auto& mode = (*it)->second;
+        if (anchorGroup == mode->getGroup()) {
+            return mode;
         }
     }
+
     ALOGE("Can't find max refresh rate by policy with the same mode group"
           " as the current mode %s",
-          mCurrentRefreshRate->toString().c_str());
-    // Defaulting to the highest refresh rate
-    return *mPrimaryRefreshRates.back();
+          to_string(*mActiveModeIt->second).c_str());
+
+    // Default to the highest refresh rate.
+    return mPrimaryRefreshRates.back()->second;
 }
 
-RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const {
+DisplayModePtr RefreshRateConfigs::getActiveMode() const {
     std::lock_guard lock(mLock);
-    return *mCurrentRefreshRate;
+    return mActiveModeIt->second;
 }
 
-RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
-    std::lock_guard lock(mLock);
-    return getCurrentRefreshRateByPolicyLocked();
-}
-
-const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
-    if (std::find(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
-                  mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
-        return *mCurrentRefreshRate;
-    }
-    return *mRefreshRates.at(getCurrentPolicyLocked()->defaultMode);
-}
-
-void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
+void RefreshRateConfigs::setActiveModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
 
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
     mGetBestRefreshRateCache.reset();
 
-    mCurrentRefreshRate = mRefreshRates.at(modeId).get();
+    mActiveModeIt = mDisplayModes.find(modeId);
+    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 }
 
-RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
+RefreshRateConfigs::RefreshRateConfigs(DisplayModes modes, DisplayModeId activeModeId,
                                        Config config)
       : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     initializeIdleTimer();
-    updateDisplayModes(modes, currentModeId);
+    updateDisplayModes(std::move(modes), activeModeId);
 }
 
 void RefreshRateConfigs::initializeIdleTimer() {
@@ -688,64 +716,43 @@
     }
 }
 
-void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
-                                            DisplayModeId currentModeId) {
+void RefreshRateConfigs::updateDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
     std::lock_guard lock(mLock);
 
-    // The current mode should be supported
-    LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
-        return mode->getId() == currentModeId;
-    }));
-
     // Invalidate the cached invocation to getBestRefreshRate. This forces
     // the refresh rate to be recomputed on the next call to getBestRefreshRate.
     mGetBestRefreshRateCache.reset();
 
-    mRefreshRates.clear();
-    for (const auto& mode : modes) {
-        const auto modeId = mode->getId();
-        mRefreshRates.emplace(modeId,
-                              std::make_unique<RefreshRate>(mode, RefreshRate::ConstructorTag(0)));
-        if (modeId == currentModeId) {
-            mCurrentRefreshRate = mRefreshRates.at(modeId).get();
-        }
-    }
+    mDisplayModes = std::move(modes);
+    mActiveModeIt = mDisplayModes.find(activeModeId);
+    LOG_ALWAYS_FATAL_IF(mActiveModeIt == mDisplayModes.end());
 
-    std::vector<const RefreshRate*> sortedModes;
-    getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes);
+    const auto sortedModes =
+            sortByRefreshRate(mDisplayModes, [](const DisplayMode&) { return true; });
+    mMinRefreshRateModeIt = sortedModes.front();
+    mMaxRefreshRateModeIt = sortedModes.back();
+
     // Reset the policy because the old one may no longer be valid.
     mDisplayManagerPolicy = {};
-    mDisplayManagerPolicy.defaultMode = currentModeId;
-    mMinSupportedRefreshRate = sortedModes.front();
-    mMaxSupportedRefreshRate = sortedModes.back();
+    mDisplayManagerPolicy.defaultMode = activeModeId;
 
-    mSupportsFrameRateOverrideByContent = false;
-    if (mConfig.enableFrameRateOverride) {
-        for (const auto& mode1 : sortedModes) {
-            for (const auto& mode2 : sortedModes) {
-                if (getFrameRateDivisor(mode1->getFps(), mode2->getFps()) >= 2) {
-                    mSupportsFrameRateOverrideByContent = true;
-                    break;
-                }
-            }
-        }
-    }
+    mSupportsFrameRateOverrideByContent =
+            mConfig.enableFrameRateOverride && canModesSupportFrameRateOverride(sortedModes);
 
     constructAvailableRefreshRates();
 }
 
 bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const {
     // defaultMode must be a valid mode, and within the given refresh rate range.
-    auto iter = mRefreshRates.find(policy.defaultMode);
-    if (iter == mRefreshRates.end()) {
+    if (const auto mode = mDisplayModes.get(policy.defaultMode)) {
+        if (!policy.primaryRange.includes(mode->get()->getFps())) {
+            ALOGE("Default mode is not in the primary range.");
+            return false;
+        }
+    } else {
         ALOGE("Default mode is not found.");
         return false;
     }
-    const RefreshRate& refreshRate = *iter->second;
-    if (!policy.primaryRange.includes(refreshRate.getFps())) {
-        ALOGE("Default mode is not in the primary range.");
-        return false;
-    }
 
     using namespace fps_approx_ops;
     return policy.appRequestRange.min <= policy.primaryRange.min &&
@@ -799,77 +806,46 @@
 
 bool RefreshRateConfigs::isModeAllowed(DisplayModeId modeId) const {
     std::lock_guard lock(mLock);
-    for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
-        if (refreshRate->getModeId() == modeId) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void RefreshRateConfigs::getSortedRefreshRateListLocked(
-        const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
-        std::vector<const RefreshRate*>* outRefreshRates) {
-    outRefreshRates->clear();
-    outRefreshRates->reserve(mRefreshRates.size());
-    for (const auto& [type, refreshRate] : mRefreshRates) {
-        if (shouldAddRefreshRate(*refreshRate)) {
-            ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy",
-                  refreshRate->getModeId().value());
-            outRefreshRates->push_back(refreshRate.get());
-        }
-    }
-
-    std::sort(outRefreshRates->begin(), outRefreshRates->end(),
-              [](const auto refreshRate1, const auto refreshRate2) {
-                  if (refreshRate1->mode->getVsyncPeriod() !=
-                      refreshRate2->mode->getVsyncPeriod()) {
-                      return refreshRate1->mode->getVsyncPeriod() >
-                              refreshRate2->mode->getVsyncPeriod();
-                  } else {
-                      return refreshRate1->mode->getGroup() > refreshRate2->mode->getGroup();
-                  }
-              });
+    return std::any_of(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
+                       [modeId](DisplayModeIterator modeIt) {
+                           return modeIt->second->getId() == modeId;
+                       });
 }
 
 void RefreshRateConfigs::constructAvailableRefreshRates() {
-    // Filter modes based on current policy and sort based on vsync period
+    // Filter modes based on current policy and sort on refresh rate.
     const Policy* policy = getCurrentPolicyLocked();
-    const auto& defaultMode = mRefreshRates.at(policy->defaultMode)->mode;
-    ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str());
+    ALOGV("%s: %s ", __func__, policy->toString().c_str());
 
-    auto filterRefreshRates =
-            [&](FpsRange range, const char* rangeName,
-                std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock) {
-                getSortedRefreshRateListLocked(
-                        [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
-                            const auto& mode = refreshRate.mode;
+    const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
 
-                            return mode->getHeight() == defaultMode->getHeight() &&
-                                    mode->getWidth() == defaultMode->getWidth() &&
-                                    mode->getDpiX() == defaultMode->getDpiX() &&
-                                    mode->getDpiY() == defaultMode->getDpiY() &&
-                                    (policy->allowGroupSwitching ||
-                                     mode->getGroup() == defaultMode->getGroup()) &&
-                                    range.includes(mode->getFps());
-                        },
-                        outRefreshRates);
+    const auto filterRefreshRates = [&](FpsRange range, const char* rangeName) REQUIRES(mLock) {
+        const auto filter = [&](const DisplayMode& mode) {
+            return mode.getResolution() == defaultMode->getResolution() &&
+                    mode.getDpi() == defaultMode->getDpi() &&
+                    (policy->allowGroupSwitching || mode.getGroup() == defaultMode->getGroup()) &&
+                    range.includes(mode.getFps());
+        };
 
-                LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(), "No matching modes for %s range %s",
-                                    rangeName, to_string(range).c_str());
+        const auto modes = sortByRefreshRate(mDisplayModes, filter);
+        LOG_ALWAYS_FATAL_IF(modes.empty(), "No matching modes for %s range %s", rangeName,
+                            to_string(range).c_str());
 
-                auto stringifyRefreshRates = [&]() -> std::string {
-                    std::string str;
-                    for (auto refreshRate : *outRefreshRates) {
-                        base::StringAppendF(&str, "%s ", refreshRate->getName().c_str());
-                    }
-                    return str;
-                };
-                ALOGV("%s refresh rates: %s", rangeName, stringifyRefreshRates().c_str());
-            };
+        const auto stringifyModes = [&] {
+            std::string str;
+            for (const auto modeIt : modes) {
+                str += to_string(modeIt->second->getFps());
+                str.push_back(' ');
+            }
+            return str;
+        };
+        ALOGV("%s refresh rates: %s", rangeName, stringifyModes().c_str());
 
-    filterRefreshRates(policy->primaryRange, "primary", &mPrimaryRefreshRates);
-    filterRefreshRates(policy->appRequestRange, "app request", &mAppRequestRefreshRates);
+        return modes;
+    };
+
+    mPrimaryRefreshRates = filterRefreshRates(policy->primaryRange, "primary");
+    mAppRequestRefreshRates = filterRefreshRates(policy->appRequestRange, "app request");
 }
 
 Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
@@ -893,36 +869,39 @@
 
 RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
     std::lock_guard lock(mLock);
-    const auto& deviceMin = *mMinSupportedRefreshRate;
-    const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
-    const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
-    const auto& currentPolicy = getCurrentPolicyLocked();
+
+    const Fps deviceMinFps = mMinRefreshRateModeIt->second->getFps();
+    const DisplayModePtr& minByPolicy = getMinRefreshRateByPolicyLocked();
 
     // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
     // the min allowed refresh rate is higher than the device min, we do not want to enable the
     // timer.
-    if (deviceMin < minByPolicy) {
-        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    if (isStrictlyLess(deviceMinFps, minByPolicy->getFps())) {
+        return KernelIdleTimerAction::TurnOff;
     }
+
+    const DisplayModePtr& maxByPolicy = getMaxRefreshRateByPolicyLocked();
     if (minByPolicy == maxByPolicy) {
-        // when min primary range in display manager policy is below device min turn on the timer.
-        if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
-            return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+        // Turn on the timer when the min of the primary range is below the device min.
+        if (const Policy* currentPolicy = getCurrentPolicyLocked();
+            isApproxLess(currentPolicy->primaryRange.min, deviceMinFps)) {
+            return KernelIdleTimerAction::TurnOn;
         }
-        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+        return KernelIdleTimerAction::TurnOff;
     }
+
     // Turn on the timer in all other cases.
-    return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+    return KernelIdleTimerAction::TurnOn;
 }
 
-int RefreshRateConfigs::getFrameRateDivisor(Fps displayFrameRate, Fps layerFrameRate) {
+int RefreshRateConfigs::getFrameRateDivisor(Fps displayRefreshRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
 
     // The threshold must be smaller than 0.001 in order to differentiate
     // between the fractional pairs (e.g. 59.94 and 60).
     constexpr float kThreshold = 0.0009f;
-    const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
+    const auto numPeriods = displayRefreshRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
         return 0;
@@ -952,29 +931,32 @@
                             currentPolicy.toString().c_str());
     }
 
-    auto mode = mCurrentRefreshRate->mode;
-    base::StringAppendF(&result, "Current mode: %s\n", mCurrentRefreshRate->toString().c_str());
+    base::StringAppendF(&result, "Active mode: %s\n", to_string(*mActiveModeIt->second).c_str());
 
-    result.append("Refresh rates:\n");
-    for (const auto& [id, refreshRate] : mRefreshRates) {
-        mode = refreshRate->mode;
-        base::StringAppendF(&result, "\t%s\n", refreshRate->toString().c_str());
+    result.append("Display modes:\n");
+    for (const auto& [id, mode] : mDisplayModes) {
+        result.push_back('\t');
+        result.append(to_string(*mode));
+        result.push_back('\n');
     }
 
     base::StringAppendF(&result, "Supports Frame Rate Override By Content: %s\n",
                         mSupportsFrameRateOverrideByContent ? "yes" : "no");
-    base::StringAppendF(&result, "Idle timer: ");
-    if (mConfig.kernelIdleTimerController.has_value()) {
-        if (mConfig.kernelIdleTimerController == KernelIdleTimerController::Sysprop) {
-            base::StringAppendF(&result, "(kernel(sysprop))");
-        } else {
-            base::StringAppendF(&result, "(kernel(hwc))");
-        }
+
+    result.append("Idle timer: ");
+    if (const auto controller = mConfig.kernelIdleTimerController) {
+        base::StringAppendF(&result, "(kernel via %s) ", ftl::enum_string(*controller).c_str());
     } else {
-        base::StringAppendF(&result, "(platform)");
+        result.append("(platform) ");
     }
-    base::StringAppendF(&result, " %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
-    result.append("\n");
+
+    if (mIdleTimer) {
+        result.append(mIdleTimer->dump());
+    } else {
+        result.append("off");
+    }
+
+    result.append("\n\n");
 }
 
 std::chrono::milliseconds RefreshRateConfigs::getIdleTimerTimeout() {