SurfaceFlinger: add support for FrameRateCompatibilityType

Bug: 147516364
Test: adb shell /data/nativetest64/SurfaceFlinger_test/SurfaceFlinger_test --gtest_filter='SetFrameRateTest.*'
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest --gtest_filter=*RefreshRateConfigs*
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest --gtest_filter=*LayerHistory*
Change-Id: I49272804e25f04e1d7a148a0008551cbc5428011
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b976523..9aada11 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
-    if (layer.getFrameRate().has_value()) {
+    if (layer.getFrameRate().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -126,18 +126,27 @@
         // Only use the layer if the reference still exists.
         if (layer || CC_UNLIKELY(mTraceEnabled)) {
             // Check if frame rate was set on layer.
-            auto frameRate = layer->getFrameRate();
-            if (frameRate.has_value() && frameRate.value() > 0.f) {
-                summary.push_back(
-                        {layer->getName(), LayerVoteType::Explicit, *frameRate, /* weight */ 1.0f});
+            const auto frameRate = layer->getFrameRate();
+            if (frameRate.rate > 0.f) {
+                const auto voteType = [&]() {
+                    switch (frameRate.type) {
+                        case Layer::FrameRateCompatibility::Default:
+                            return LayerVoteType::ExplicitDefault;
+                        case Layer::FrameRateCompatibility::ExactOrMultiple:
+                            return LayerVoteType::ExplicitExactOrMultiple;
+                        case Layer::FrameRateCompatibility::NoVote:
+                            return LayerVoteType::NoVote;
+                    }
+                }();
+                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
             } else if (recent) {
-                frameRate = info->getRefreshRate(now);
-                summary.push_back({layer->getName(), LayerVoteType::Heuristic, *frameRate,
+                summary.push_back({layer->getName(), LayerVoteType::Heuristic,
+                                   info->getRefreshRate(now),
                                    /* weight */ 1.0f});
             }
 
             if (CC_UNLIKELY(mTraceEnabled)) {
-                trace(weakLayer, round<int>(*frameRate));
+                trace(weakLayer, round<int>(frameRate.rate));
             }
         }
     }
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index a6d2c74..ce085f4 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
-    if (layer.getFrameRate().has_value()) {
+    if (layer.getFrameRate().rate > 0) {
         return layer.isVisible();
     }
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -63,13 +63,17 @@
     const auto& name = layer->getName();
     const auto noVoteTag = "LFPS NoVote " + name;
     const auto heuristicVoteTag = "LFPS Heuristic " + name;
-    const auto explicitVoteTag = "LFPS Explicit " + name;
+    const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
+    const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
     const auto minVoteTag = "LFPS Min " + name;
     const auto maxVoteTag = "LFPS Max " + name;
 
     ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
     ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
-    ATRACE_INT(explicitVoteTag.c_str(), type == LayerHistory::LayerVoteType::Explicit ? fps : 0);
+    ATRACE_INT(explicitDefaultVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
+    ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
     ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
     ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
 
@@ -160,12 +164,18 @@
             i++;
             // Set layer vote if set
             const auto frameRate = layer->getFrameRate();
-            if (frameRate.has_value()) {
-                if (*frameRate == Layer::FRAME_RATE_NO_VOTE) {
-                    info->setLayerVote(LayerVoteType::NoVote, 0.f);
-                } else {
-                    info->setLayerVote(LayerVoteType::Explicit, *frameRate);
+            const auto voteType = [&]() {
+                switch (frameRate.type) {
+                    case Layer::FrameRateCompatibility::Default:
+                        return LayerVoteType::ExplicitDefault;
+                    case Layer::FrameRateCompatibility::ExactOrMultiple:
+                        return LayerVoteType::ExplicitExactOrMultiple;
+                    case Layer::FrameRateCompatibility::NoVote:
+                        return LayerVoteType::NoVote;
                 }
+            }();
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                info->setLayerVote(voteType, frameRate.rate);
             } else {
                 info->resetLayerVote();
             }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index d2af912..c73e825 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -37,7 +37,8 @@
     int explicitContentFramerate = 0;
     for (const auto& layer : layers) {
         const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
-        if (layer.vote == LayerVoteType::Explicit) {
+        if (layer.vote == LayerVoteType::ExplicitDefault ||
+            layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
             if (desiredRefreshRateRound > explicitContentFramerate) {
                 explicitContentFramerate = desiredRefreshRateRound;
             }
@@ -94,7 +95,8 @@
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
-    int explicitVoteLayers = 0;
+    int explicitDefaultVoteLayers = 0;
+    int explicitExactOrMultipleVoteLayers = 0;
     for (const auto& layer : layers) {
         if (layer.vote == LayerVoteType::NoVote)
             noVoteLayers++;
@@ -102,8 +104,10 @@
             minVoteLayers++;
         else if (layer.vote == LayerVoteType::Max)
             maxVoteLayers++;
-        else if (layer.vote == LayerVoteType::Explicit)
-            explicitVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitDefault)
+            explicitDefaultVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+            explicitExactOrMultipleVoteLayers++;
     }
 
     // Only if all layers want Min we should return Min
@@ -112,7 +116,7 @@
     }
 
     // If we have some Max layers and no Explicit we should return Max
-    if (maxVoteLayers > 0 && explicitVoteLayers == 0) {
+    if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
         return *mAvailableRefreshRates.back();
     }
 
@@ -131,9 +135,22 @@
             continue;
         }
 
-        // If we have Explicit layers, ignore the Hueristic ones
-        if (explicitVoteLayers > 0 && layer.vote == LayerVoteType::Heuristic) {
-            continue;
+        // Adjust the weight in case we have explicit layers. The priority is:
+        //  - ExplicitExactOrMultiple
+        //  - ExplicitDefault
+        //  - Heuristic
+        auto weight = layer.weight;
+        if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic) {
+                weight /= 2.f;
+            }
+        }
+
+        if (explicitExactOrMultipleVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic ||
+                layer.vote == LayerVoteType::ExplicitDefault) {
+                weight /= 2.f;
+            }
         }
 
         for (auto& [refreshRate, overallScore] : scores) {
@@ -152,10 +169,10 @@
             static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
             if (displayFramesRem == 0) {
                 // Layer desired refresh rate matches the display rate.
-                layerScore = layer.weight * 1.0f;
+                layerScore = weight * 1.0f;
             } else if (displayFramesQuot == 0) {
                 // Layer desired refresh rate is higher the display rate.
-                layerScore = layer.weight *
+                layerScore = weight *
                         (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
                         (1.0f / (MAX_FRAMES_TO_FIT + 1));
             } else {
@@ -168,11 +185,11 @@
                     iter++;
                 }
 
-                layerScore = layer.weight * (1.0f / iter);
+                layerScore = weight * (1.0f / iter);
             }
 
-            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
-                  layer.weight, 1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
+                  1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
             overallScore += layerScore;
         }
     }
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index c762efd..fc95959 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -96,11 +96,14 @@
 
     // Describes the different options the layer voted for refresh rate
     enum class LayerVoteType {
-        NoVote,    // Doesn't care about the refresh rate
-        Min,       // Minimal refresh rate available
-        Max,       // Maximal refresh rate available
-        Heuristic, // Specific refresh rate that was calculated by platform using a heuristic
-        Explicit,  // Specific refresh rate that was provided by the app
+        NoVote,          // Doesn't care about the refresh rate
+        Min,             // Minimal refresh rate available
+        Max,             // Maximal refresh rate available
+        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
+        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+                         // compatibility
+        ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
+                                // ExactOrMultiple compatibility
     };
 
     // Captures the layer requirements for a refresh rate. This will be used to determine the
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 258ce0a..52fb6f3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -408,7 +408,8 @@
                         "SurfaceView - "
                         "com.google.android.youtube/"
                         "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
-                layer->setFrameRate(vote);
+                layer->setFrameRate(
+                        Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
             }
         }
     }
@@ -562,7 +563,8 @@
 
 bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
     for (const auto& layer : mFeatures.contentRequirements) {
-        if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::Explicit) {
+        if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault ||
+            layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple) {
             return true;
         }
     }