SF: avoid changing refresh rate for ExplicitExact

When a layer with ExplicitExact vote is present and the corresponding
refresh rate is not available, the refresh rate should remain the same.

Bug: 246230302
Test: SF unit tests
Change-Id: I3e1712d6494fc9fc46b8d26fdae310231f57e7b1
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c10b817..30483a2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -314,7 +314,8 @@
     // Keep the display at max refresh rate for the duration of powering on the display.
     if (signals.powerOnImminent) {
         ALOGV("Power On Imminent");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.powerOnImminent = true}};
     }
 
@@ -374,7 +375,8 @@
     // selected a refresh rate to see if we should apply touch boost.
     if (signals.touch && !hasExplicitVoteLayers) {
         ALOGV("Touch Boost");
-        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.touch = true}};
     }
 
@@ -386,20 +388,23 @@
 
     if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
         ALOGV("Idle");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 GlobalSignals{.idle = true}};
     }
 
     if (layers.empty() || noVoteLayers == layers.size()) {
         ALOGV("No layers with votes");
-        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 kNoSignals};
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
         ALOGV("All layers Min");
-        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending),
+        return {getRefreshRatesByPolicyLocked(activeMode.getGroup(), RefreshRateOrder::Ascending,
+                                              /*preferredDisplayModeOpt*/ std::nullopt),
                 kNoSignals};
     }
 
@@ -560,13 +565,17 @@
                        return RefreshRateRanking{score.modeIt->second, score.overallScore};
                    });
 
+    const bool noLayerScore = std::all_of(scores.begin(), scores.end(), [](RefreshRateScore score) {
+        return score.overallScore == 0;
+    });
+
     if (primaryRangeIsSingleRate) {
         // If we never scored any layers, then choose the rate from the primary
         // range instead of picking a random score from the app range.
-        if (std::all_of(scores.begin(), scores.end(),
-                        [](RefreshRateScore score) { return score.overallScore == 0; })) {
+        if (noLayerScore) {
             ALOGV("Layers not scored");
-            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending),
+            return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                                  /*preferredDisplayModeOpt*/ std::nullopt),
                     kNoSignals};
         } else {
             return {rankedRefreshRates, kNoSignals};
@@ -588,7 +597,8 @@
     }();
 
     const auto& touchRefreshRates =
-            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending);
+            getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Descending,
+                                          /*preferredDisplayModeOpt*/ std::nullopt);
     using fps_approx_ops::operator<;
 
     if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
@@ -598,6 +608,15 @@
         return {touchRefreshRates, GlobalSignals{.touch = true}};
     }
 
+    // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
+    // current config
+    if (noLayerScore && refreshRateOrder == RefreshRateOrder::Ascending) {
+        const auto preferredDisplayMode = activeMode.getId();
+        return {getRefreshRatesByPolicyLocked(anchorGroup, RefreshRateOrder::Ascending,
+                                              preferredDisplayMode),
+                kNoSignals};
+    }
+
     return {rankedRefreshRates, kNoSignals};
 }
 
@@ -765,15 +784,29 @@
 }
 
 std::vector<RefreshRateRanking> RefreshRateConfigs::getRefreshRatesByPolicyLocked(
-        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder) const {
-    std::vector<RefreshRateRanking> rankings;
+        std::optional<int> anchorGroupOpt, RefreshRateOrder refreshRateOrder,
+        std::optional<DisplayModeId> preferredDisplayModeOpt) const {
+    std::deque<RefreshRateRanking> rankings;
     const auto makeRanking = [&](const DisplayModeIterator it) REQUIRES(mLock) {
         const auto& mode = it->second;
-        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
-        const float score = calculateRefreshRateScoreForFps(mode->getFps());
-        if (!anchorGroupOpt || mode->getGroup() == anchorGroupOpt) {
-            rankings.push_back(RefreshRateRanking{mode, inverseScore ? 1.0f / score : score});
+        if (anchorGroupOpt && mode->getGroup() != anchorGroupOpt) {
+            return;
         }
+
+        float score = calculateRefreshRateScoreForFps(mode->getFps());
+        const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
+        if (inverseScore) {
+            score = 1.0f / score;
+        }
+        if (preferredDisplayModeOpt) {
+            if (*preferredDisplayModeOpt == mode->getId()) {
+                rankings.push_front(RefreshRateRanking{mode, /*score*/ 1.0f});
+                return;
+            }
+            constexpr float kNonPreferredModePenalty = 0.95f;
+            score *= kNonPreferredModePenalty;
+        }
+        rankings.push_back(RefreshRateRanking{mode, score});
     };
 
     if (refreshRateOrder == RefreshRateOrder::Ascending) {
@@ -783,14 +816,15 @@
     }
 
     if (!rankings.empty() || !anchorGroupOpt) {
-        return rankings;
+        return {rankings.begin(), rankings.end()};
     }
 
     ALOGW("Can't find %s refresh rate by policy with the same mode group"
           " as the mode group %d",
           refreshRateOrder == RefreshRateOrder::Ascending ? "min" : "max", anchorGroupOpt.value());
 
-    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder);
+    return getRefreshRatesByPolicyLocked(/*anchorGroupOpt*/ std::nullopt, refreshRateOrder,
+                                         preferredDisplayModeOpt);
 }
 
 DisplayModePtr RefreshRateConfigs::getActiveModePtr() const {