Add GTE compatibility logic for NoVote

setFrameRate with GTE compatibility and 0 `frameRate` will be counted as NoVote.

Test: atest libsurfaceflinger_unittest
Test: atest LayerHistoryIntegrationTest
Bug: 380949716
Flag: com.android.graphics.surfaceflinger.flags.arr_setframerate_gte_enum
Change-Id: I5ffcd4a09b1661c516c11d2f04b80303af215c3a
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 171342d..630beb0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -280,6 +280,9 @@
                     case Layer::FrameRateCompatibility::Exact:
                         return LayerVoteType::ExplicitExact;
                     case Layer::FrameRateCompatibility::Gte:
+                        if (frameRate.isNoVote()) {
+                            return LayerVoteType::NoVote;
+                        }
                         if (isVrrDevice) {
                             return LayerVoteType::ExplicitGte;
                         } else {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 356c30e..6e2b943 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -562,7 +562,10 @@
 }
 
 bool LayerInfo::FrameRate::isNoVote() const {
-    return vote.type == FrameRateCompatibility::NoVote;
+    // A desired frame rate greater than or equal to 0 is treated as NoVote.
+    bool isNoVoteGte = FlagManager::getInstance().arr_setframerate_gte_enum() &&
+            vote.type == FrameRateCompatibility::Gte && !vote.rate.isValid();
+    return vote.type == FrameRateCompatibility::NoVote || isNoVoteGte;
 }
 
 bool LayerInfo::FrameRate::isValuelessType() const {
@@ -577,7 +580,12 @@
         case FrameRateCompatibility::Default:
         case FrameRateCompatibility::ExactOrMultiple:
         case FrameRateCompatibility::Exact:
+            return false;
         case FrameRateCompatibility::Gte:
+            if (isNoVote()) {
+                // Special case: GTE 0 is same as NoVote.
+                return true;
+            }
             return false;
     }
 }
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index f9bfe97..53a9062 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -654,6 +654,72 @@
     EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
 }
 
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_arr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // Set the test to be on a vrr mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+    mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGteNoVote_mrr) {
+    SET_FLAG_FOR_TEST(flags::arr_setframerate_gte_enum, true);
+    // True by default on MRR devices as well, but the device is not set to VRR mode.
+    SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+    auto layer = createLegacyAndFrontedEndLayer(1);
+    showLayer(1);
+    setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_GTE,
+                 ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+    setFrameRateCategory(1, 0);
+
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        setBufferWithPresentTime(layer, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // Layer is active but GTE with 0 should be considered NoVote, thus nothing from summarize.
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive.
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+    EXPECT_EQ(0u, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
     SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);