Make shouldBeSeamless an enum

We change theboolean shouldBeSemaless to an enum with
three values. This introduces a third value "Default" which
indicates that the layer doesn't have a preference for
seamlessness. This is the default value for Surfaces which
haven't called setFrameRate, or have called setFrameRate(0).

Bug: 161776961
Test: presubmit
Change-Id: I157e332e82e95badc928d6a8135e657cd6984db4
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b6b754b..ab0d3df 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1687,9 +1687,9 @@
                   crop.bottom);
     if (layerState.frameRate.rate != 0 ||
         layerState.frameRate.type != FrameRateCompatibility::Default) {
-        StringAppendF(&result, "% 6.2ffps %15s seamless=%d", layerState.frameRate.rate,
+        StringAppendF(&result, "% 6.2ffps %15s seamless=%s", layerState.frameRate.rate,
                       frameRateCompatibilityString(layerState.frameRate.type).c_str(),
-                      layerState.frameRate.shouldBeSeamless);
+                      toString(layerState.frameRate.seamlessness).c_str());
     } else {
         StringAppendF(&result, "                         ");
     }
@@ -2754,7 +2754,7 @@
 std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
     return stream << "{rate=" << rate.rate
                   << " type=" << Layer::frameRateCompatibilityString(rate.type)
-                  << " shouldBeSeamless=" << rate.shouldBeSeamless << "}";
+                  << " seamlessness=" << toString(rate.seamlessness) << "}";
 }
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8d67ce5..75d68a1 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -49,7 +49,9 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "RenderArea.h"
+#include "Scheduler/Seamlessness.h"
 #include "SurfaceFlinger.h"
+#include "SurfaceTracing.h"
 #include "TransactionCompletedThread.h"
 
 using namespace android::surfaceflinger;
@@ -151,17 +153,21 @@
     // Encapsulates the frame rate and compatibility of the layer. This information will be used
     // when the display refresh rate is determined.
     struct FrameRate {
+        using Seamlessness = scheduler::Seamlessness;
+
         float rate;
         FrameRateCompatibility type;
-        bool shouldBeSeamless;
+        Seamlessness seamlessness;
 
-        FrameRate() : rate(0), type(FrameRateCompatibility::Default), shouldBeSeamless(true) {}
+        FrameRate()
+              : rate(0),
+                type(FrameRateCompatibility::Default),
+                seamlessness(Seamlessness::Default) {}
         FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
-              : rate(rate), type(type), shouldBeSeamless(shouldBeSeamless) {}
+              : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {}
 
         bool operator==(const FrameRate& other) const {
-            return rate == other.rate && type == other.type &&
-                    shouldBeSeamless == other.shouldBeSeamless;
+            return rate == other.rate && type == other.type && seamlessness == other.seamlessness;
         }
 
         bool operator!=(const FrameRate& other) const { return !(*this == other); }
@@ -169,6 +175,19 @@
         // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
         // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
         static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+
+    private:
+        static Seamlessness getSeamlessness(float rate, bool shouldBeSeamless) {
+            if (rate == 0.0f) {
+                // Refresh rate of 0 is a special value which should reset the vote to
+                // its default value.
+                return Seamlessness::Default;
+            } else if (shouldBeSeamless) {
+                return Seamlessness::OnlySeamless;
+            } else {
+                return Seamlessness::SeamedAndSeamless;
+            }
+        }
     };
 
     struct State {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 28af930..359ee26 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -129,8 +129,7 @@
                         RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
                                                              .vote = voteType,
                                                              .desiredRefreshRate = frameRate.rate,
-                                                             .shouldBeSeamless =
-                                                                     frameRate.shouldBeSeamless,
+                                                             .seamlessness = frameRate.seamlessness,
                                                              .weight = 1.0f,
                                                              .focused = layerFocused});
             } else if (recent) {
@@ -139,7 +138,8 @@
                                                              .vote = LayerVoteType::Heuristic,
                                                              .desiredRefreshRate =
                                                                      info->getRefreshRate(now),
-                                                             .shouldBeSeamless = true,
+                                                             .seamlessness =
+                                                                     Seamlessness::OnlySeamless,
                                                              .weight = 1.0f,
                                                              .focused = layerFocused});
             }
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index a63ccc1..e919d1b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -144,8 +144,8 @@
 
         const float layerArea = transformed.getWidth() * transformed.getHeight();
         float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        summary.push_back({strong->getName(), vote.type, vote.fps, vote.shouldBeSeamless, weight,
-                           layerFocused});
+        summary.push_back(
+                {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
 
         if (CC_UNLIKELY(mTraceEnabled)) {
             trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps)));
@@ -179,7 +179,7 @@
 
             if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
                 const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
-                info->setLayerVote({type, frameRate.rate, frameRate.shouldBeSeamless});
+                info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
             } else {
                 info->resetLayerVote();
             }
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index 2305bc3..f94f4ab 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -23,6 +23,7 @@
 
 #include "LayerHistory.h"
 #include "RefreshRateConfigs.h"
+#include "Scheduler/Seamlessness.h"
 #include "SchedulerUtils.h"
 
 namespace android {
@@ -60,7 +61,7 @@
     struct LayerVote {
         LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
         float fps = 0.0f;
-        bool shouldBeSeamless = true;
+        Seamlessness seamlessness = Seamlessness::Default;
     };
 
     static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
@@ -91,7 +92,7 @@
     void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
 
     // Resets the layer vote to its default.
-    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, true}; }
+    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, Seamlessness::Default}; }
 
     LayerVote getRefreshRateVote(nsecs_t now);
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b872d7a..4ebab3e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -27,6 +27,14 @@
 #define LOG_TAG "RefreshRateConfigs"
 
 namespace android::scheduler {
+namespace {
+std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
+    return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %.2fHz",
+                              layer.name.c_str(),
+                              RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight,
+                              toString(layer.seamlessness).c_str(), layer.desiredRefreshRate);
+}
+} // namespace
 
 using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
 using RefreshRate = RefreshRateConfigs::RefreshRate;
@@ -170,7 +178,7 @@
             maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
         }
 
-        if (!layer.shouldBeSeamless) {
+        if (layer.seamlessness == Seamlessness::SeamedAndSeamless) {
             seamedLayers++;
         }
     }
@@ -229,27 +237,38 @@
         auto weight = layer.weight;
 
         for (auto i = 0u; i < scores.size(); i++) {
-            // If there are no layers with shouldBeSeamless=false and the current
-            // config group is different from the default one, this means a layer with
-            // shouldBeSeamless=false has just disappeared and we should switch back to
-            // the default config group.
-            const bool isSeamlessSwitch = seamedLayers > 0
-                    ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
-                    : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+            const bool isSeamlessSwitch =
+                    scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup();
 
-            if (layer.shouldBeSeamless && !isSeamlessSwitch) {
-                ALOGV("%s (weight %.2f) ignores %s (group=%d) to avoid non-seamless switch."
-                      "Current config = %s",
-                      layer.name.c_str(), weight, scores[i].first->name.c_str(),
-                      scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+            if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
+                ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
+                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      mCurrentRefreshRate->toString().c_str());
                 continue;
             }
 
-            if (!layer.shouldBeSeamless && !isSeamlessSwitch && !layer.focused) {
-                ALOGV("%s (weight %.2f) ignores %s (group=%d) because it's not focused"
-                      " and the switch is going to be seamed. Current config = %s",
-                      layer.name.c_str(), weight, scores[i].first->name.c_str(),
-                      scores[i].first->getConfigGroup(), mCurrentRefreshRate->toString().c_str());
+            if (layer.seamlessness == Seamlessness::SeamedAndSeamless && !isSeamlessSwitch &&
+                !layer.focused) {
+                ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
+                      " Current config = %s",
+                      formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+                      mCurrentRefreshRate->toString().c_str());
+                continue;
+            }
+
+            // Layers with default seamlessness vote for the current config group if
+            // there are layers with seamlessness=SeamedAndSeamless and for the default
+            // config group otherwise. In second case, if the current config group is different
+            // from the default, this means a layer with seamlessness=SeamedAndSeamless has just
+            // disappeared.
+            const bool isInPolicyForDefault = seamedLayers > 0
+                    ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
+                    : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+
+            if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
+                !layer.focused) {
+                ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
+                      scores[i].first->toString().c_str(), mCurrentRefreshRate->toString().c_str());
                 continue;
             }
 
@@ -267,7 +286,7 @@
                 const auto ratio = scores[i].first->fps / scores.back().first->fps;
                 // use ratio^2 to get a lower score the more we get further from peak
                 const auto layerScore = ratio * ratio;
-                ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
+                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
                       scores[i].first->name.c_str(), layerScore);
                 scores[i].second += weight * layerScore;
                 continue;
@@ -290,9 +309,8 @@
                                             static_cast<float>(actualLayerPeriod));
                 }();
 
-                ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
-                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
-                      layerScore);
+                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+                      scores[i].first->name.c_str(), layerScore);
                 scores[i].second += weight * layerScore;
                 continue;
             }
@@ -332,8 +350,7 @@
                 // Slightly prefer seamless switches.
                 constexpr float kSeamedSwitchPenalty = 0.95f;
                 const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
-                ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
-                      layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
+                ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
                       scores[i].first->name.c_str(), layerScore);
                 scores[i].second += weight * layerScore * seamlessness;
                 continue;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 3159352..2ef8f0c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -26,6 +26,7 @@
 #include "DisplayHardware/HWComposer.h"
 #include "HwcStrongTypes.h"
 #include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/Seamlessness.h"
 #include "Scheduler/StrongTyping.h"
 
 namespace android::scheduler {
@@ -219,7 +220,7 @@
         // Layer's desired refresh rate, if applicable.
         float desiredRefreshRate = 0.0f;
         // If a seamless mode switch is required.
-        bool shouldBeSeamless = true;
+        Seamlessness seamlessness = Seamlessness::Default;
         // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
         // would have on choosing the refresh rate.
         float weight = 0.0f;
diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/Seamlessness.h
new file mode 100644
index 0000000..3e42a4d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Seamlessness.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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 <cstring>
+#include <ostream>
+
+namespace android {
+namespace scheduler {
+
+// The seamlessness requirement of a Layer.
+enum class Seamlessness {
+    // Indicates a requirement for a seamless mode switch.
+    OnlySeamless,
+    // Indicates that both seamless and seamed mode switches are allowed.
+    SeamedAndSeamless,
+    // Indicates no preference for seamlessness. For such layers the system will
+    // prefer seamless switches, but also non-seamless switches to the group of the
+    // default config are allowed.
+    Default
+};
+
+inline std::string toString(Seamlessness seamlessness) {
+    switch (seamlessness) {
+        case Seamlessness::OnlySeamless:
+            return "OnlySeamless";
+        case Seamlessness::SeamedAndSeamless:
+            return "SeamedAndSeamless";
+        case Seamlessness::Default:
+            return "Default";
+    }
+}
+
+// Used by gtest
+inline std::ostream& operator<<(std::ostream& os, Seamlessness val) {
+    return os << toString(val);
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 604a83a..dbfa881 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -606,8 +606,7 @@
     return *mCompositionEngine.get();
 }
 
-void SurfaceFlinger::bootFinished()
-{
+void SurfaceFlinger::bootFinished() {
     if (mBootFinished == true) {
         ALOGE("Extra call to bootFinished");
         return;
@@ -2105,8 +2104,7 @@
     getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency;
 }
 
-void SurfaceFlinger::postComposition()
-{
+void SurfaceFlinger::postComposition() {
     ATRACE_CALL();
     ALOGV("postComposition");
 
@@ -2295,8 +2293,7 @@
     }
 }
 
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
-{
+void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) {
     ATRACE_CALL();
 
     // here we keep a copy of the drawing state (that is the state that's
@@ -2658,8 +2655,7 @@
     mDrawingState.displays = mCurrentState.displays;
 }
 
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
-{
+void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
     const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
 
     // Notify all layers of available frames
@@ -3010,8 +3006,7 @@
     }
 }
 
-bool SurfaceFlinger::handlePageFlip()
-{
+bool SurfaceFlinger::handlePageFlip() {
     ATRACE_CALL();
     ALOGV("handlePageFlip");
 
@@ -3091,8 +3086,7 @@
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
 
-void SurfaceFlinger::invalidateHwcGeometry()
-{
+void SurfaceFlinger::invalidateHwcGeometry() {
     mGeometryInvalid = true;
 }
 
@@ -4072,8 +4066,7 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer)
-{
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
     Mutex::Autolock lock(mStateLock);
     // If a layer has a parent, we allow it to out-live it's handle
     // with the idea that the parent holds a reference and will eventually
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 3b50321..7bfec9a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -73,12 +73,11 @@
                              [now](const auto& pair) { return pair.second->isAnimating(now); });
     }
 
-    void setLayerInfoVote(Layer* layer,
-                          LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+    void setDefaultLayerVote(Layer* layer,
+                             LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
         for (auto& [weak, info] : history().mLayerInfos) {
             if (auto strong = weak.promote(); strong && strong.get() == layer) {
                 info->setDefaultLayerVote(vote);
-                info->setLayerVote({vote, 0, false});
                 return;
             }
         }
@@ -209,7 +208,7 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -236,7 +235,7 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -264,7 +263,7 @@
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
@@ -310,7 +309,7 @@
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive, but the vote stays
-    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
     ASSERT_EQ(1, history().summarize(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
@@ -343,7 +342,7 @@
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer became inactive, but the vote stays
-    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
     time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
     ASSERT_EQ(1, history().summarize(time).size());
     EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index df76110..5a92d0a 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -310,7 +310,7 @@
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
     const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
-        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*shouldBeSeamless*/ true,
+        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, Seamlessness::OnlySeamless,
                  /*weight*/ 1.0f, /*focused*/ false}};
     };
 
@@ -1263,7 +1263,7 @@
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitDefault;
     layer.desiredRefreshRate = 90.0f;
-    layer.shouldBeSeamless = false;
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "90Hz ExplicitDefault";
     layer.focused = true;
 
@@ -1280,32 +1280,49 @@
                       .getConfigId());
 
     // Verify that we won't change the group if seamless switch is required.
-    layer.shouldBeSeamless = true;
+    layer.seamlessness = Seamlessness::OnlySeamless;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getConfigId());
 
-    // At this point the default config in the DisplayManager policy with be 60Hz.
-    // Verify that if the current config is in another group and there are no layers with
-    // shouldBeSeamless=false we'll go back to the default group.
+    // Verify that we won't do a seamless switch if we request the same mode as the default
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
     layer.desiredRefreshRate = 60.0f;
     layer.name = "60Hz ExplicitDefault";
-    layer.shouldBeSeamless = true;
+    layer.seamlessness = Seamlessness::OnlySeamless;
+    ASSERT_EQ(HWC_CONFIG_ID_90,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getConfigId());
+
+    // Verify that if the current config is in another group and there are no layers with
+    // seamlessness=SeamedAndSeamless we'll go back to the default group.
+    layer.desiredRefreshRate = 60.0f;
+    layer.name = "60Hz ExplicitDefault";
+    layer.seamlessness = Seamlessness::Default;
     ASSERT_EQ(HWC_CONFIG_ID_60,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getConfigId());
 
-    // If there's a layer with shouldBeSeamless=false, another layer with shouldBeSeamless=true
-    // can't change the config group.
+    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
+    // seamlessness=OnlySeamless can't change the config group.
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
-    auto layer2 = LayerRequirement{.weight = 0.5f};
+    layer.seamlessness = Seamlessness::OnlySeamless;
+
+    layers.push_back(LayerRequirement{.weight = 0.5f});
+    auto& layer2 = layers[layers.size() - 1];
     layer2.vote = LayerVoteType::ExplicitDefault;
     layer2.desiredRefreshRate = 90.0f;
     layer2.name = "90Hz ExplicitDefault";
-    layer2.shouldBeSeamless = false;
+    layer2.seamlessness = Seamlessness::SeamedAndSeamless;
     layer2.focused = false;
-    layers.push_back(layer2);
+
+    ASSERT_EQ(HWC_CONFIG_ID_90,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getConfigId());
+
+    // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
+    // seamlessness=Default can't change the config group.
+    layers[0].seamlessness = Seamlessness::Default;
     ASSERT_EQ(HWC_CONFIG_ID_90,
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
                       .getConfigId());
@@ -1326,7 +1343,7 @@
     auto& layer = layers[0];
     layer.vote = LayerVoteType::ExplicitExactOrMultiple;
     layer.desiredRefreshRate = 60.0f;
-    layer.shouldBeSeamless = false;
+    layer.seamlessness = Seamlessness::SeamedAndSeamless;
     layer.name = "60Hz ExplicitExactOrMultiple";
     layer.focused = true;
 
@@ -1355,13 +1372,13 @@
             LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
                                                .vote = LayerVoteType::ExplicitDefault,
                                                .desiredRefreshRate = 60.0f,
-                                               .shouldBeSeamless = false,
+                                               .seamlessness = Seamlessness::SeamedAndSeamless,
                                                .weight = 0.5f,
                                                .focused = false},
                               LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
                                                .vote = LayerVoteType::ExplicitExactOrMultiple,
                                                .desiredRefreshRate = 25.0f,
-                                               .shouldBeSeamless = true,
+                                               .seamlessness = Seamlessness::OnlySeamless,
                                                .weight = 1.0f,
                                                .focused = true}};
     auto& seamedLayer = layers[0];