Merge changes from topic "deprecate-content-detection-v1"

* changes:
  Move LayerHistoryV2 to LayerHistory
  SF: Deprecate content detection v1
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index ed0d75b..fdb8aaf 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -156,9 +156,7 @@
         "Scheduler/EventThread.cpp",
         "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
-        "Scheduler/LayerHistoryV2.cpp",
         "Scheduler/LayerInfo.cpp",
-        "Scheduler/LayerInfoV2.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/RefreshRateConfigs.cpp",
         "Scheduler/Scheduler.cpp",
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 359ee26..c0d00f3 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -35,21 +35,21 @@
 #include "LayerInfo.h"
 #include "SchedulerUtils.h"
 
-namespace android::scheduler::impl {
+namespace android::scheduler {
 
 namespace {
 
 bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+    // Layers with an explicit vote are always kept active
     if (layer.getFrameRateForLayerTree().rate > 0) {
-        return layer.isVisible();
+        return true;
     }
+
     return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
 }
 
 bool traceEnabled() {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.layer_history_trace", value, "0");
-    return atoi(value);
+    return property_get_bool("debug.sf.layer_history_trace", false);
 }
 
 bool useFrameRatePriority() {
@@ -58,30 +58,44 @@
     return atoi(value);
 }
 
-void trace(const wp<Layer>& weak, int fps) {
+void trace(const wp<Layer>& weak, const LayerInfo& info, LayerHistory::LayerVoteType type,
+           int fps) {
     const auto layer = weak.promote();
     if (!layer) return;
 
-    const auto& name = layer->getName();
-    const auto tag = "LFPS " + name;
-    ATRACE_INT(tag.c_str(), fps);
-    ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+    };
+
+    traceType(LayerHistory::LayerVoteType::NoVote, 1);
+    traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+    traceType(LayerHistory::LayerVoteType::Min, 1);
+    traceType(LayerHistory::LayerVoteType::Max, 1);
+
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
 }
 } // namespace
 
-LayerHistory::LayerHistory()
-      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs)
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+    LayerInfo::setTraceEnabled(mTraceEnabled);
+    LayerInfo::setRefreshRateConfigs(refreshRateConfigs);
+}
+
 LayerHistory::~LayerHistory() = default;
 
-void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
-                                 LayerVoteType /*type*/) {
-    auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
+void LayerHistory::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
+                                 LayerVoteType type) {
+    const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
+    auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type);
     std::lock_guard lock(mLock);
     mLayerInfos.emplace_back(layer, std::move(info));
 }
 
 void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
-                          LayerUpdateType /*updateType*/) {
+                          LayerUpdateType updateType) {
     std::lock_guard lock(mLock);
 
     const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -89,7 +103,7 @@
     LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
 
     const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now);
+    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
 
     // Activate layer if inactive.
     if (const auto end = activeLayers().end(); it >= end) {
@@ -99,54 +113,42 @@
 }
 
 LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
-    ATRACE_CALL();
+    LayerHistory::Summary summary;
+
     std::lock_guard lock(mLock);
 
     partitionLayers(now);
 
-    LayerHistory::Summary summary;
-    for (const auto& [weakLayer, info] : activeLayers()) {
-        const bool recent = info->isRecentlyActive(now);
-        auto layer = weakLayer.promote();
-        // Only use the layer if the reference still exists.
-        if (layer || CC_UNLIKELY(mTraceEnabled)) {
-            const auto layerFocused =
-                    Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
-            // Check if frame rate was set on layer.
-            const auto frameRate = layer->getFrameRateForLayerTree();
-            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(
-                        RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
-                                                             .vote = voteType,
-                                                             .desiredRefreshRate = frameRate.rate,
-                                                             .seamlessness = frameRate.seamlessness,
-                                                             .weight = 1.0f,
-                                                             .focused = layerFocused});
-            } else if (recent) {
-                summary.push_back(
-                        RefreshRateConfigs::LayerRequirement{.name = layer->getName(),
-                                                             .vote = LayerVoteType::Heuristic,
-                                                             .desiredRefreshRate =
-                                                                     info->getRefreshRate(now),
-                                                             .seamlessness =
-                                                                     Seamlessness::OnlySeamless,
-                                                             .weight = 1.0f,
-                                                             .focused = layerFocused});
-            }
+    for (const auto& [layer, info] : activeLayers()) {
+        const auto strong = layer.promote();
+        if (!strong) {
+            continue;
+        }
 
-            if (CC_UNLIKELY(mTraceEnabled)) {
-                trace(weakLayer, round<int>(frameRate.rate));
-            }
+        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+        const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
+              frameRateSelectionPriority, layerFocused ? "" : "not");
+
+        const auto vote = info->getRefreshRateVote(now);
+        // Skip NoVote layer as those don't have any requirements
+        if (vote.type == LayerHistory::LayerVoteType::NoVote) {
+            continue;
+        }
+
+        // Compute the layer's position on the screen
+        const Rect bounds = Rect(strong->getBounds());
+        const ui::Transform transform = strong->getTransform();
+        constexpr bool roundOutwards = true;
+        Rect transformed = transform.transform(bounds, roundOutwards);
+
+        const float layerArea = transformed.getWidth() * transformed.getHeight();
+        float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+        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)));
         }
     }
 
@@ -162,14 +164,33 @@
         auto& [weak, info] = mLayerInfos[i];
         if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
             i++;
+            // Set layer vote if set
+            const auto frameRate = layer->getFrameRateForLayerTree();
+            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) {
+                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
+            } else {
+                info->resetLayerVote();
+            }
             continue;
         }
 
         if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, 0);
+            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
         }
 
-        info->clearHistory();
+        info->onLayerInactive(now);
         std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
     }
 
@@ -190,10 +211,8 @@
     std::lock_guard lock(mLock);
 
     for (const auto& [layer, info] : activeLayers()) {
-        info->clearHistory();
+        info->clearHistory(systemTime());
     }
-
-    mActiveLayersEnd = 0;
 }
 
 std::string LayerHistory::dump() const {
@@ -202,4 +221,4 @@
                               mActiveLayersEnd);
 }
 
-} // namespace android::scheduler::impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 128699b..507ccc6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -36,25 +36,23 @@
 namespace scheduler {
 
 class LayerHistoryTest;
-class LayerHistoryTestV2;
 class LayerInfo;
-class LayerInfoV2;
 
 class LayerHistory {
 public:
     using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 
-    virtual ~LayerHistory() = default;
+    LayerHistory(const RefreshRateConfigs&);
+    ~LayerHistory();
 
     // Layers are unregistered when the weak reference expires.
-    virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
-                               LayerVoteType type) = 0;
+    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate, LayerVoteType type);
 
     // Sets the display size. Client is responsible for synchronization.
-    virtual void setDisplayArea(uint32_t displayArea) = 0;
+    void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
 
     // Sets whether a config change is pending to be applied
-    virtual void setConfigChangePending(bool pending) = 0;
+    void setConfigChangePending(bool pending) { mConfigChangePending = pending; }
 
     // Represents which layer activity is recorded
     enum class LayerUpdateType {
@@ -64,45 +62,18 @@
     };
 
     // Marks the layer as active, and records the given state to its history.
-    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType);
 
     using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    virtual Summary summarize(nsecs_t now) = 0;
+    Summary summarize(nsecs_t now);
 
-    virtual void clear() = 0;
-    virtual std::string dump() const = 0;
-};
-
-namespace impl {
-// Records per-layer history of scheduling-related information (primarily present time),
-// heuristically categorizes layers as active or inactive, and summarizes stats about
-// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
-class LayerHistory : public android::scheduler::LayerHistory {
-public:
-    LayerHistory();
-    virtual ~LayerHistory();
-
-    // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
-                       LayerVoteType type) override;
-
-    void setDisplayArea(uint32_t /*displayArea*/) override {}
-
-    void setConfigChangePending(bool /*pending*/) override {}
-
-    // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
-    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
-
-    void clear() override;
-    std::string dump() const override;
+    void clear();
+    std::string dump() const;
 
 private:
-    friend class android::scheduler::LayerHistoryTest;
+    friend LayerHistoryTest;
     friend TestableScheduler;
 
     using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
@@ -130,65 +101,6 @@
     LayerInfos mLayerInfos GUARDED_BY(mLock);
     size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
 
-    // Whether to emit systrace output and debug logs.
-    const bool mTraceEnabled;
-
-    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
-    const bool mUseFrameRatePriority;
-};
-
-class LayerHistoryV2 : public android::scheduler::LayerHistory {
-public:
-    LayerHistoryV2(const scheduler::RefreshRateConfigs&);
-    virtual ~LayerHistoryV2();
-
-    // Layers are unregistered when the weak reference expires.
-    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
-                       LayerVoteType type) override;
-
-    // Sets the display size. Client is responsible for synchronization.
-    void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
-
-    void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
-
-    // Marks the layer as active, and records the given state to its history.
-    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
-
-    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
-    android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
-
-    void clear() override;
-    std::string dump() const override;
-
-private:
-    friend android::scheduler::LayerHistoryTestV2;
-    friend TestableScheduler;
-
-    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
-    using LayerInfos = std::vector<LayerPair>;
-
-    struct ActiveLayers {
-        LayerInfos& infos;
-        const size_t index;
-
-        auto begin() { return infos.begin(); }
-        auto end() { return begin() + static_cast<long>(index); }
-    };
-
-    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
-    // Iterates over layers in a single pass, swapping pairs such that active layers precede
-    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
-    // truncating after inactive layers.
-    void partitionLayers(nsecs_t now) REQUIRES(mLock);
-
-    mutable std::mutex mLock;
-
-    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
-    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
-    LayerInfos mLayerInfos GUARDED_BY(mLock);
-    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
-
     uint32_t mDisplayArea = 0;
 
     // Whether to emit systrace output and debug logs.
@@ -201,6 +113,5 @@
     std::atomic<bool> mConfigChangePending = false;
 };
 
-} // namespace impl
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
deleted file mode 100644
index e919d1b..0000000
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryV2"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerHistory.h"
-
-#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <cmath>
-#include <string>
-#include <utility>
-
-#include "../Layer.h"
-#include "LayerInfoV2.h"
-#include "SchedulerUtils.h"
-
-namespace android::scheduler::impl {
-
-namespace {
-
-bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
-    // Layers with an explicit vote are always kept active
-    if (layer.getFrameRateForLayerTree().rate > 0) {
-        return true;
-    }
-
-    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
-}
-
-bool traceEnabled() {
-    return property_get_bool("debug.sf.layer_history_trace", false);
-}
-
-bool useFrameRatePriority() {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.use_frame_rate_priority", value, "1");
-    return atoi(value);
-}
-
-void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
-           int fps) {
-    const auto layer = weak.promote();
-    if (!layer) return;
-
-    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
-        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
-    };
-
-    traceType(LayerHistory::LayerVoteType::NoVote, 1);
-    traceType(LayerHistory::LayerVoteType::Heuristic, fps);
-    traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
-    traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
-    traceType(LayerHistory::LayerVoteType::Min, 1);
-    traceType(LayerHistory::LayerVoteType::Max, 1);
-
-    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
-}
-} // namespace
-
-LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
-      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
-    LayerInfoV2::setTraceEnabled(mTraceEnabled);
-    LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
-}
-
-LayerHistoryV2::~LayerHistoryV2() = default;
-
-void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
-                                   LayerVoteType type) {
-    const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
-    auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
-    std::lock_guard lock(mLock);
-    mLayerInfos.emplace_back(layer, std::move(info));
-}
-
-void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
-                            LayerUpdateType updateType) {
-    std::lock_guard lock(mLock);
-
-    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
-                                 [layer](const auto& pair) { return pair.first == layer; });
-    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
-    const auto& info = it->second;
-    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
-
-    // Activate layer if inactive.
-    if (const auto end = activeLayers().end(); it >= end) {
-        std::iter_swap(it, end);
-        mActiveLayersEnd++;
-    }
-}
-
-LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
-    LayerHistory::Summary summary;
-
-    std::lock_guard lock(mLock);
-
-    partitionLayers(now);
-
-    for (const auto& [layer, info] : activeLayers()) {
-        const auto strong = layer.promote();
-        if (!strong) {
-            continue;
-        }
-
-        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
-        const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
-        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
-              frameRateSelectionPriority, layerFocused ? "" : "not");
-
-        const auto vote = info->getRefreshRateVote(now);
-        // Skip NoVote layer as those don't have any requirements
-        if (vote.type == LayerHistory::LayerVoteType::NoVote) {
-            continue;
-        }
-
-        // Compute the layer's position on the screen
-        const Rect bounds = Rect(strong->getBounds());
-        const ui::Transform transform = strong->getTransform();
-        constexpr bool roundOutwards = true;
-        Rect transformed = transform.transform(bounds, roundOutwards);
-
-        const float layerArea = transformed.getWidth() * transformed.getHeight();
-        float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
-        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)));
-        }
-    }
-
-    return summary;
-}
-
-void LayerHistoryV2::partitionLayers(nsecs_t now) {
-    const nsecs_t threshold = getActiveLayerThreshold(now);
-
-    // Collect expired and inactive layers after active layers.
-    size_t i = 0;
-    while (i < mActiveLayersEnd) {
-        auto& [weak, info] = mLayerInfos[i];
-        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
-            i++;
-            // Set layer vote if set
-            const auto frameRate = layer->getFrameRateForLayerTree();
-            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) {
-                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
-                info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
-            } else {
-                info->resetLayerVote();
-            }
-            continue;
-        }
-
-        if (CC_UNLIKELY(mTraceEnabled)) {
-            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
-        }
-
-        info->onLayerInactive(now);
-        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
-    }
-
-    // Collect expired layers after inactive layers.
-    size_t end = mLayerInfos.size();
-    while (i < end) {
-        if (mLayerInfos[i].first.promote()) {
-            i++;
-        } else {
-            std::swap(mLayerInfos[i], mLayerInfos[--end]);
-        }
-    }
-
-    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
-}
-
-void LayerHistoryV2::clear() {
-    std::lock_guard lock(mLock);
-
-    for (const auto& [layer, info] : activeLayers()) {
-        info->clearHistory(systemTime());
-    }
-}
-
-std::string LayerHistoryV2::dump() const {
-    std::lock_guard lock(mLock);
-    return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(),
-                              mActiveLayersEnd);
-}
-
-} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 6d9dd43..66ac98a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,36 +14,284 @@
  * limitations under the License.
  */
 
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include "LayerInfo.h"
 
 #include <algorithm>
 #include <utility>
 
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfo"
+
 namespace android::scheduler {
 
-LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
-      : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
+const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
+bool LayerInfo::sTraceEnabled = false;
 
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+LayerInfo::LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
+                     LayerHistory::LayerVoteType defaultVote)
+      : mName(name),
+        mHighRefreshRatePeriod(highRefreshRatePeriod),
+        mDefaultVote(defaultVote),
+        mLayerVote({defaultVote, 0.0f}),
+        mRefreshRateHistory(name) {}
+
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+                                   bool pendingConfigChange) {
     lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
-    // Buffers can come with a present time far in the future. That keeps them relevant.
     mLastUpdatedTime = std::max(lastPresentTime, now);
-    mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
+    switch (updateType) {
+        case LayerUpdateType::AnimationTX:
+            mLastAnimationTime = std::max(lastPresentTime, now);
+            break;
+        case LayerUpdateType::SetFrameRate:
+        case LayerUpdateType::Buffer:
+            FrameTimeData frameTime = {.presetTime = lastPresentTime,
+                                       .queueTime = mLastUpdatedTime,
+                                       .pendingConfigChange = pendingConfigChange};
+            mFrameTimes.push_back(frameTime);
+            if (mFrameTimes.size() > HISTORY_SIZE) {
+                mFrameTimes.pop_front();
+            }
+            break;
+    }
+}
 
-    if (mLastPresentTime == 0) {
-        // First frame
-        mLastPresentTime = lastPresentTime;
-        return;
+bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
+    return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                          mFrameTimeValidSince.time_since_epoch())
+                                          .count();
+}
+
+bool LayerInfo::isFrequent(nsecs_t now) const {
+    // If we know nothing about this layer we consider it as frequent as it might be the start
+    // of an animation.
+    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+        return true;
     }
 
-    const nsecs_t period = lastPresentTime - mLastPresentTime;
-    mLastPresentTime = lastPresentTime;
-    // Ignore time diff that are too high - those are stale values
-    if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+    // Find the first active frame
+    auto it = mFrameTimes.begin();
+    for (; it != mFrameTimes.end(); ++it) {
+        if (it->queueTime >= getActiveLayerThreshold(now)) {
+            break;
+        }
+    }
 
-    const float fps = std::min(1e9f / period, mHighRefreshRate);
-    mRefreshRateHistory.insertRefreshRate(fps);
+    const auto numFrames = std::distance(it, mFrameTimes.end());
+    if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
+        return false;
+    }
+
+    // Layer is considered frequent if the average frame rate is higher than the threshold
+    const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
+    return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
+}
+
+bool LayerInfo::isAnimating(nsecs_t now) const {
+    return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfo::hasEnoughDataForHeuristic() const {
+    // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
+    if (mFrameTimes.size() < 2) {
+        ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
+        return false;
+    }
+
+    if (!isFrameTimeValid(mFrameTimes.front())) {
+        ALOGV("stale frames still captured");
+        return false;
+    }
+
+    const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+    if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+        ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+              totalDuration / 1e9f);
+        return false;
+    }
+
+    return true;
+}
+
+std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const {
+    nsecs_t totalPresentTimeDeltas = 0;
+    nsecs_t totalQueueTimeDeltas = 0;
+    bool missingPresentTime = false;
+    int numFrames = 0;
+    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        // Ignore frames captured during a config change
+        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+            return std::nullopt;
+        }
+
+        totalQueueTimeDeltas +=
+                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+        numFrames++;
+
+        if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
+            missingPresentTime = true;
+            // If there are no presentation timestamps and we haven't calculated
+            // one in the past then we can't calculate the refresh rate
+            if (mLastRefreshRate.reported == 0) {
+                return std::nullopt;
+            }
+            continue;
+        }
+
+        totalPresentTimeDeltas +=
+                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+    }
+
+    // Calculate the average frame time based on presentation timestamps. If those
+    // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+    // we calculated a refresh rate based on presentation timestamps in the past. The reason
+    // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+    // when implementing render ahead for specific refresh rates. When hwui no longer provides
+    // presentation timestamps we look at the queue time to see if the current refresh rate still
+    // matches the content.
+
+    const auto averageFrameTime =
+            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+            numFrames;
+    return static_cast<nsecs_t>(averageFrameTime);
+}
+
+std::optional<float> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) {
+    static constexpr float MARGIN = 1.0f; // 1Hz
+    if (!hasEnoughDataForHeuristic()) {
+        ALOGV("Not enough data");
+        return std::nullopt;
+    }
+
+    const auto averageFrameTime = calculateAverageFrameTime();
+    if (averageFrameTime.has_value()) {
+        const auto refreshRate = 1e9f / *averageFrameTime;
+        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+        if (refreshRateConsistent) {
+            const auto knownRefreshRate =
+                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
+
+            // To avoid oscillation, use the last calculated refresh rate if it is
+            // close enough
+            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+                mLastRefreshRate.reported != knownRefreshRate) {
+                mLastRefreshRate.calculated = refreshRate;
+                mLastRefreshRate.reported = knownRefreshRate;
+            }
+
+            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        } else {
+            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        }
+    }
+
+    return mLastRefreshRate.reported == 0 ? std::nullopt
+                                          : std::make_optional(mLastRefreshRate.reported);
+}
+
+LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) {
+    if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+        ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+        return mLayerVote;
+    }
+
+    if (isAnimating(now)) {
+        ALOGV("%s is animating", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
+        return {LayerHistory::LayerVoteType::Max, 0};
+    }
+
+    if (!isFrequent(now)) {
+        ALOGV("%s is infrequent", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
+        return {LayerHistory::LayerVoteType::Min, 0};
+    }
+
+    // If the layer was previously tagged as animating or infrequent, we clear
+    // the history as it is likely the layer just changed its behavior
+    // and we should not look at stale data
+    if (mLastRefreshRate.animatingOrInfrequent) {
+        clearHistory(now);
+    }
+
+    auto refreshRate = calculateRefreshRateIfPossible(now);
+    if (refreshRate.has_value()) {
+        ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
+        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+    }
+
+    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
+    return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+    if (mTraceTags.count(type) == 0) {
+        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+        mTraceTags.emplace(type, tag);
+    }
+
+    return mTraceTags.at(type).c_str();
+}
+
+LayerInfo::RefreshRateHistory::HeuristicTraceTagData
+LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const {
+    const std::string prefix = "LFPS ";
+    const std::string suffix = "Heuristic ";
+    return {.min = prefix + mName + suffix + "min",
+            .max = prefix + mName + suffix + "max",
+            .consistent = prefix + mName + suffix + "consistent",
+            .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfo::RefreshRateHistory::clear() {
+    mRefreshRates.clear();
+}
+
+bool LayerInfo::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+    mRefreshRates.push_back({refreshRate, now});
+    while (mRefreshRates.size() >= HISTORY_SIZE ||
+           now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+        mRefreshRates.pop_front();
+    }
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+    }
+
+    return isConsistent();
+}
+
+bool LayerInfo::RefreshRateHistory::isConsistent() const {
+    if (mRefreshRates.empty()) return true;
+
+    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+    }
+
+    return consistent;
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 820624b..e434670 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -21,6 +21,9 @@
 #include <chrono>
 #include <deque>
 
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "Scheduler/Seamlessness.h"
 #include "SchedulerUtils.h"
 
 namespace android {
@@ -41,129 +44,179 @@
 
 // Stores history of present times and refresh rates for a layer.
 class LayerInfo {
+    using LayerUpdateType = LayerHistory::LayerUpdateType;
+
     // Layer is considered frequent if the earliest value in the window of most recent present times
     // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
     // favor of a low refresh rate.
     static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
-    static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
-
-    /**
-     * Struct that keeps the information about the refresh rate for last
-     * HISTORY_SIZE frames. This is used to better determine the refresh rate
-     * for individual layers.
-     */
-    class RefreshRateHistory {
-    public:
-        explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
-
-        void insertRefreshRate(float refreshRate) {
-            mElements.push_back(refreshRate);
-            if (mElements.size() > HISTORY_SIZE) {
-                mElements.pop_front();
-            }
-        }
-
-        float getRefreshRateAvg() const {
-            return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
-        }
-
-        void clearHistory() { mElements.clear(); }
-
-    private:
-        const float mHighRefreshRate;
-
-        static constexpr size_t HISTORY_SIZE = 30;
-        std::deque<float> mElements;
-    };
-
-    /**
-     * Struct that keeps the information about the present time for last
-     * HISTORY_SIZE frames. This is used to better determine whether the given layer
-     * is still relevant and it's refresh rate should be considered.
-     */
-    class PresentTimeHistory {
-    public:
-        static constexpr size_t HISTORY_SIZE = 90;
-
-        void insertPresentTime(nsecs_t presentTime) {
-            mElements.push_back(presentTime);
-            if (mElements.size() > HISTORY_SIZE) {
-                mElements.pop_front();
-            }
-        }
-
-        // Returns whether the earliest present time is within the active threshold.
-        bool isRecentlyActive(nsecs_t now) const {
-            if (mElements.size() < 2) {
-                return false;
-            }
-
-            // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
-            if (mElements.size() < HISTORY_SIZE &&
-                mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
-                return false;
-            }
-
-            return mElements.back() >= getActiveLayerThreshold(now);
-        }
-
-        bool isFrequent(nsecs_t now) const {
-            // Assume layer is infrequent if too few present times have been recorded.
-            if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
-                return false;
-            }
-
-            // Layer is frequent if the earliest value in the window of most recent present times is
-            // within threshold.
-            const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
-            const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
-            return *it >= threshold;
-        }
-
-        void clearHistory() { mElements.clear(); }
-
-    private:
-        std::deque<nsecs_t> mElements;
-        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
-    };
+    static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
+            std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
 
     friend class LayerHistoryTest;
 
 public:
-    LayerInfo(float lowRefreshRate, float highRefreshRate);
+    // Holds information about the layer vote
+    struct LayerVote {
+        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+        float fps = 0.0f;
+        Seamlessness seamlessness = Seamlessness::Default;
+    };
+
+    static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+        sRefreshRateConfigs = &refreshRateConfigs;
+    }
+
+    LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
+              LayerHistory::LayerVoteType defaultVote);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
 
-    // Records the last requested oresent time. It also stores information about when
+    // Records the last requested present time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+                            bool pendingConfigChange);
 
-    bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
-    bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
+    // Sets an explicit layer vote. This usually comes directly from the application via
+    // ANativeWindow_setFrameRate API
+    void setLayerVote(LayerVote vote) { mLayerVote = vote; }
 
-    float getRefreshRate(nsecs_t now) const {
-        return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
-    }
+    // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+    // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+    // layer can go back to whatever vote it had before the app voted for it.
+    void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+    // Resets the layer vote to its default.
+    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, Seamlessness::Default}; }
+
+    LayerVote getRefreshRateVote(nsecs_t now);
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
     nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
-    void clearHistory() {
-        mRefreshRateHistory.clearHistory();
-        mPresentTimeHistory.clearHistory();
+    // Returns a C string for tracing a vote
+    const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
+    void onLayerInactive(nsecs_t now) {
+        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+        // We are not deleting the old frame to keep track of whether we should treat the first
+        // buffer as Max as we don't know anything about this layer or Min as this layer is
+        // posting infrequent updates.
+        const auto timePoint = std::chrono::nanoseconds(now);
+        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+        mLastRefreshRate = {};
+        mRefreshRateHistory.clear();
+    }
+
+    void clearHistory(nsecs_t now) {
+        onLayerInactive(now);
+        mFrameTimes.clear();
     }
 
 private:
-    const float mLowRefreshRate;
-    const float mHighRefreshRate;
+    // Used to store the layer timestamps
+    struct FrameTimeData {
+        nsecs_t presetTime; // desiredPresentTime, if provided
+        nsecs_t queueTime;  // buffer queue time
+        bool pendingConfigChange;
+    };
+
+    // Holds information about the calculated and reported refresh rate
+    struct RefreshRateHeuristicData {
+        // Rate calculated on the layer
+        float calculated = 0.0f;
+        // Last reported rate for LayerInfo::getRefreshRate()
+        float reported = 0.0f;
+        // Whether the last reported rate for LayerInfo::getRefreshRate()
+        // was due to animation or infrequent updates
+        bool animatingOrInfrequent = false;
+    };
+
+    // Class to store past calculated refresh rate and determine whether
+    // the refresh rate calculated is consistent with past values
+    class RefreshRateHistory {
+    public:
+        static constexpr auto HISTORY_SIZE = 90;
+        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+        RefreshRateHistory(const std::string& name) : mName(name) {}
+
+        // Clears History
+        void clear();
+
+        // Adds a new refresh rate and returns true if it is consistent
+        bool add(float refreshRate, nsecs_t now);
+
+    private:
+        friend class LayerHistoryTest;
+
+        // Holds the refresh rate when it was calculated
+        struct RefreshRateData {
+            float refreshRate = 0.0f;
+            nsecs_t timestamp = 0;
+
+            bool operator<(const RefreshRateData& other) const {
+                return refreshRate < other.refreshRate;
+            }
+        };
+
+        // Holds tracing strings
+        struct HeuristicTraceTagData {
+            std::string min;
+            std::string max;
+            std::string consistent;
+            std::string average;
+        };
+
+        bool isConsistent() const;
+        HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+        const std::string mName;
+        mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+        std::deque<RefreshRateData> mRefreshRates;
+        static constexpr float MARGIN_FPS = 1.0;
+    };
+
+    bool isFrequent(nsecs_t now) const;
+    bool isAnimating(nsecs_t now) const;
+    bool hasEnoughDataForHeuristic() const;
+    std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+    std::optional<nsecs_t> calculateAverageFrameTime() const;
+    bool isFrameTimeValid(const FrameTimeData&) const;
+
+    const std::string mName;
+
+    // Used for sanitizing the heuristic data
+    const nsecs_t mHighRefreshRatePeriod;
+    LayerHistory::LayerVoteType mDefaultVote;
+
+    LayerVote mLayerVote;
 
     nsecs_t mLastUpdatedTime = 0;
-    nsecs_t mLastPresentTime = 0;
-    RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
-    PresentTimeHistory mPresentTimeHistory;
+
+    nsecs_t mLastAnimationTime = 0;
+
+    RefreshRateHeuristicData mLastRefreshRate;
+
+    std::deque<FrameTimeData> mFrameTimes;
+    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+            std::chrono::steady_clock::now();
+    static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+    static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+    RefreshRateHistory mRefreshRateHistory;
+
+    mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+    // Shared for all LayerInfo instances
+    static const RefreshRateConfigs* sRefreshRateConfigs;
+    static bool sTraceEnabled;
 };
 
 } // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
deleted file mode 100644
index 94e7e20..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * 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.
- */
-
-// #define LOG_NDEBUG 0
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerInfoV2.h"
-
-#include <algorithm>
-#include <utility>
-
-#include <cutils/compiler.h>
-#include <cutils/trace.h>
-
-#undef LOG_TAG
-#define LOG_TAG "LayerInfoV2"
-
-namespace android::scheduler {
-
-const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
-bool LayerInfoV2::sTraceEnabled = false;
-
-LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
-                         LayerHistory::LayerVoteType defaultVote)
-      : mName(name),
-        mHighRefreshRatePeriod(highRefreshRatePeriod),
-        mDefaultVote(defaultVote),
-        mLayerVote({defaultVote, 0.0f}),
-        mRefreshRateHistory(name) {}
-
-void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
-                                     LayerUpdateType updateType, bool pendingConfigChange) {
-    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
-
-    mLastUpdatedTime = std::max(lastPresentTime, now);
-    switch (updateType) {
-        case LayerUpdateType::AnimationTX:
-            mLastAnimationTime = std::max(lastPresentTime, now);
-            break;
-        case LayerUpdateType::SetFrameRate:
-        case LayerUpdateType::Buffer:
-            FrameTimeData frameTime = {.presetTime = lastPresentTime,
-                                       .queueTime = mLastUpdatedTime,
-                                       .pendingConfigChange = pendingConfigChange};
-            mFrameTimes.push_back(frameTime);
-            if (mFrameTimes.size() > HISTORY_SIZE) {
-                mFrameTimes.pop_front();
-            }
-            break;
-    }
-}
-
-bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
-    return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
-                                          mFrameTimeValidSince.time_since_epoch())
-                                          .count();
-}
-
-bool LayerInfoV2::isFrequent(nsecs_t now) const {
-    // If we know nothing about this layer we consider it as frequent as it might be the start
-    // of an animation.
-    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
-        return true;
-    }
-
-    // Find the first active frame
-    auto it = mFrameTimes.begin();
-    for (; it != mFrameTimes.end(); ++it) {
-        if (it->queueTime >= getActiveLayerThreshold(now)) {
-            break;
-        }
-    }
-
-    const auto numFrames = std::distance(it, mFrameTimes.end());
-    if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
-        return false;
-    }
-
-    // Layer is considered frequent if the average frame rate is higher than the threshold
-    const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
-    return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
-}
-
-bool LayerInfoV2::isAnimating(nsecs_t now) const {
-    return mLastAnimationTime >= getActiveLayerThreshold(now);
-}
-
-bool LayerInfoV2::hasEnoughDataForHeuristic() const {
-    // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
-    if (mFrameTimes.size() < 2) {
-        ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
-        return false;
-    }
-
-    if (!isFrameTimeValid(mFrameTimes.front())) {
-        ALOGV("stale frames still captured");
-        return false;
-    }
-
-    const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
-    if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
-        ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
-              totalDuration / 1e9f);
-        return false;
-    }
-
-    return true;
-}
-
-std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
-    nsecs_t totalPresentTimeDeltas = 0;
-    nsecs_t totalQueueTimeDeltas = 0;
-    bool missingPresentTime = false;
-    int numFrames = 0;
-    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
-        // Ignore frames captured during a config change
-        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
-            return std::nullopt;
-        }
-
-        totalQueueTimeDeltas +=
-                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
-        numFrames++;
-
-        if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
-            missingPresentTime = true;
-            // If there are no presentation timestamps and we haven't calculated
-            // one in the past then we can't calculate the refresh rate
-            if (mLastRefreshRate.reported == 0) {
-                return std::nullopt;
-            }
-            continue;
-        }
-
-        totalPresentTimeDeltas +=
-                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
-    }
-
-    // Calculate the average frame time based on presentation timestamps. If those
-    // doesn't exist, we look at the time the buffer was queued only. We can do that only if
-    // we calculated a refresh rate based on presentation timestamps in the past. The reason
-    // we look at the queue time is to handle cases where hwui attaches presentation timestamps
-    // when implementing render ahead for specific refresh rates. When hwui no longer provides
-    // presentation timestamps we look at the queue time to see if the current refresh rate still
-    // matches the content.
-
-    const auto averageFrameTime =
-            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
-            numFrames;
-    return static_cast<nsecs_t>(averageFrameTime);
-}
-
-std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
-    static constexpr float MARGIN = 1.0f; // 1Hz
-    if (!hasEnoughDataForHeuristic()) {
-        ALOGV("Not enough data");
-        return std::nullopt;
-    }
-
-    const auto averageFrameTime = calculateAverageFrameTime();
-    if (averageFrameTime.has_value()) {
-        const auto refreshRate = 1e9f / *averageFrameTime;
-        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
-        if (refreshRateConsistent) {
-            const auto knownRefreshRate =
-                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
-
-            // To avoid oscillation, use the last calculated refresh rate if it is
-            // close enough
-            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
-                mLastRefreshRate.reported != knownRefreshRate) {
-                mLastRefreshRate.calculated = refreshRate;
-                mLastRefreshRate.reported = knownRefreshRate;
-            }
-
-            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
-                  refreshRate, mLastRefreshRate.reported);
-        } else {
-            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
-                  refreshRate, mLastRefreshRate.reported);
-        }
-    }
-
-    return mLastRefreshRate.reported == 0 ? std::nullopt
-                                          : std::make_optional(mLastRefreshRate.reported);
-}
-
-LayerInfoV2::LayerVote LayerInfoV2::getRefreshRateVote(nsecs_t now) {
-    if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
-        ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
-        return mLayerVote;
-    }
-
-    if (isAnimating(now)) {
-        ALOGV("%s is animating", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
-        return {LayerHistory::LayerVoteType::Max, 0};
-    }
-
-    if (!isFrequent(now)) {
-        ALOGV("%s is infrequent", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
-        return {LayerHistory::LayerVoteType::Min, 0};
-    }
-
-    // If the layer was previously tagged as animating or infrequent, we clear
-    // the history as it is likely the layer just changed its behavior
-    // and we should not look at stale data
-    if (mLastRefreshRate.animatingOrInfrequent) {
-        clearHistory(now);
-    }
-
-    auto refreshRate = calculateRefreshRateIfPossible(now);
-    if (refreshRate.has_value()) {
-        ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
-        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
-    }
-
-    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
-    return {LayerHistory::LayerVoteType::Max, 0};
-}
-
-const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
-    if (mTraceTags.count(type) == 0) {
-        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
-        mTraceTags.emplace(type, tag);
-    }
-
-    return mTraceTags.at(type).c_str();
-}
-
-LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
-LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
-    const std::string prefix = "LFPS ";
-    const std::string suffix = "Heuristic ";
-    return {.min = prefix + mName + suffix + "min",
-            .max = prefix + mName + suffix + "max",
-            .consistent = prefix + mName + suffix + "consistent",
-            .average = prefix + mName + suffix + "average"};
-}
-
-void LayerInfoV2::RefreshRateHistory::clear() {
-    mRefreshRates.clear();
-}
-
-bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
-    mRefreshRates.push_back({refreshRate, now});
-    while (mRefreshRates.size() >= HISTORY_SIZE ||
-           now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
-        mRefreshRates.pop_front();
-    }
-
-    if (CC_UNLIKELY(sTraceEnabled)) {
-        if (!mHeuristicTraceTagData.has_value()) {
-            mHeuristicTraceTagData = makeHeuristicTraceTagData();
-        }
-
-        ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
-    }
-
-    return isConsistent();
-}
-
-bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
-    if (mRefreshRates.empty()) return true;
-
-    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
-    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
-    const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
-
-    if (CC_UNLIKELY(sTraceEnabled)) {
-        if (!mHeuristicTraceTagData.has_value()) {
-            mHeuristicTraceTagData = makeHeuristicTraceTagData();
-        }
-
-        ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
-        ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
-        ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
-    }
-
-    return consistent;
-}
-
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
deleted file mode 100644
index f94f4ab..0000000
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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 <utils/Timers.h>
-
-#include <chrono>
-#include <deque>
-
-#include "LayerHistory.h"
-#include "RefreshRateConfigs.h"
-#include "Scheduler/Seamlessness.h"
-#include "SchedulerUtils.h"
-
-namespace android {
-
-class Layer;
-
-namespace scheduler {
-
-using namespace std::chrono_literals;
-
-// Maximum period between presents for a layer to be considered active.
-constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
-
-// Earliest present time for a layer to be considered active.
-constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
-    return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
-}
-
-// Stores history of present times and refresh rates for a layer.
-class LayerInfoV2 {
-    using LayerUpdateType = LayerHistory::LayerUpdateType;
-
-    // Layer is considered frequent if the earliest value in the window of most recent present times
-    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
-    // favor of a low refresh rate.
-    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
-    static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
-    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
-            std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
-
-    friend class LayerHistoryTestV2;
-
-public:
-    // Holds information about the layer vote
-    struct LayerVote {
-        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
-        float fps = 0.0f;
-        Seamlessness seamlessness = Seamlessness::Default;
-    };
-
-    static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
-
-    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
-        sRefreshRateConfigs = &refreshRateConfigs;
-    }
-
-    LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
-                LayerHistory::LayerVoteType defaultVote);
-
-    LayerInfoV2(const LayerInfo&) = delete;
-    LayerInfoV2& operator=(const LayerInfoV2&) = delete;
-
-    // Records the last requested present time. It also stores information about when
-    // the layer was last updated. If the present time is farther in the future than the
-    // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
-                            bool pendingConfigChange);
-
-    // Sets an explicit layer vote. This usually comes directly from the application via
-    // ANativeWindow_setFrameRate API
-    void setLayerVote(LayerVote vote) { mLayerVote = vote; }
-
-    // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
-    // This is used for layers that called to setLayerVote() and then removed the vote, so that the
-    // layer can go back to whatever vote it had before the app voted for it.
-    void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
-
-    // Resets the layer vote to its default.
-    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f, Seamlessness::Default}; }
-
-    LayerVote getRefreshRateVote(nsecs_t now);
-
-    // Return the last updated time. If the present time is farther in the future than the
-    // updated time, the updated time is the present time.
-    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
-
-    // Returns a C string for tracing a vote
-    const char* getTraceTag(LayerHistory::LayerVoteType type) const;
-
-    void onLayerInactive(nsecs_t now) {
-        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
-        // We are not deleting the old frame to keep track of whether we should treat the first
-        // buffer as Max as we don't know anything about this layer or Min as this layer is
-        // posting infrequent updates.
-        const auto timePoint = std::chrono::nanoseconds(now);
-        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
-        mLastRefreshRate = {};
-        mRefreshRateHistory.clear();
-    }
-
-    void clearHistory(nsecs_t now) {
-        onLayerInactive(now);
-        mFrameTimes.clear();
-    }
-
-private:
-    // Used to store the layer timestamps
-    struct FrameTimeData {
-        nsecs_t presetTime; // desiredPresentTime, if provided
-        nsecs_t queueTime;  // buffer queue time
-        bool pendingConfigChange;
-    };
-
-    // Holds information about the calculated and reported refresh rate
-    struct RefreshRateHeuristicData {
-        // Rate calculated on the layer
-        float calculated = 0.0f;
-        // Last reported rate for LayerInfoV2::getRefreshRate()
-        float reported = 0.0f;
-        // Whether the last reported rate for LayerInfoV2::getRefreshRate()
-        // was due to animation or infrequent updates
-        bool animatingOrInfrequent = false;
-    };
-
-    // Class to store past calculated refresh rate and determine whether
-    // the refresh rate calculated is consistent with past values
-    class RefreshRateHistory {
-    public:
-        static constexpr auto HISTORY_SIZE = 90;
-        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
-
-        RefreshRateHistory(const std::string& name) : mName(name) {}
-
-        // Clears History
-        void clear();
-
-        // Adds a new refresh rate and returns true if it is consistent
-        bool add(float refreshRate, nsecs_t now);
-
-    private:
-        friend class LayerHistoryTestV2;
-
-        // Holds the refresh rate when it was calculated
-        struct RefreshRateData {
-            float refreshRate = 0.0f;
-            nsecs_t timestamp = 0;
-
-            bool operator<(const RefreshRateData& other) const {
-                return refreshRate < other.refreshRate;
-            }
-        };
-
-        // Holds tracing strings
-        struct HeuristicTraceTagData {
-            std::string min;
-            std::string max;
-            std::string consistent;
-            std::string average;
-        };
-
-        bool isConsistent() const;
-        HeuristicTraceTagData makeHeuristicTraceTagData() const;
-
-        const std::string mName;
-        mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
-        std::deque<RefreshRateData> mRefreshRates;
-        static constexpr float MARGIN_FPS = 1.0;
-    };
-
-    bool isFrequent(nsecs_t now) const;
-    bool isAnimating(nsecs_t now) const;
-    bool hasEnoughDataForHeuristic() const;
-    std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
-    std::optional<nsecs_t> calculateAverageFrameTime() const;
-    bool isFrameTimeValid(const FrameTimeData&) const;
-
-    const std::string mName;
-
-    // Used for sanitizing the heuristic data
-    const nsecs_t mHighRefreshRatePeriod;
-    LayerHistory::LayerVoteType mDefaultVote;
-
-    LayerVote mLayerVote;
-
-    nsecs_t mLastUpdatedTime = 0;
-
-    nsecs_t mLastAnimationTime = 0;
-
-    RefreshRateHeuristicData mLastRefreshRate;
-
-    std::deque<FrameTimeData> mFrameTimes;
-    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
-            std::chrono::steady_clock::now();
-    static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
-    static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
-
-    RefreshRateHistory mRefreshRateHistory;
-
-    mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
-
-    // Shared for all LayerInfo instances
-    static const RefreshRateConfigs* sRefreshRateConfigs;
-    static bool sTraceEnabled;
-};
-
-} // namespace scheduler
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b14e482..83fa20e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -69,60 +69,6 @@
                               primaryRange.max, appRequestRange.min, appRequestRange.max);
 }
 
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
-        const std::vector<LayerRequirement>& layers) const {
-    std::lock_guard lock(mLock);
-    int contentFramerate = 0;
-    int explicitContentFramerate = 0;
-    for (const auto& layer : layers) {
-        const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
-        if (layer.vote == LayerVoteType::ExplicitDefault ||
-            layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
-            if (desiredRefreshRateRound > explicitContentFramerate) {
-                explicitContentFramerate = desiredRefreshRateRound;
-            }
-        } else {
-            if (desiredRefreshRateRound > contentFramerate) {
-                contentFramerate = desiredRefreshRateRound;
-            }
-        }
-    }
-
-    if (explicitContentFramerate != 0) {
-        contentFramerate = explicitContentFramerate;
-    } else if (contentFramerate == 0) {
-        contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps());
-    }
-    ATRACE_INT("ContentFPS", contentFramerate);
-
-    // Find the appropriate refresh rate with minimal error
-    auto iter = min_element(mPrimaryRefreshRates.cbegin(), mPrimaryRefreshRates.cend(),
-                            [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
-                                return std::abs(lhs->fps - contentFramerate) <
-                                        std::abs(rhs->fps - contentFramerate);
-                            });
-
-    // Some content aligns better on higher refresh rate. For example for 45fps we should choose
-    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
-    // align well with both
-    const RefreshRate* bestSoFar = *iter;
-    constexpr float MARGIN = 0.05f;
-    float ratio = (*iter)->fps / contentFramerate;
-    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mPrimaryRefreshRates.cend()) {
-            ratio = (*iter)->fps / contentFramerate;
-
-            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
-                bestSoFar = *iter;
-                break;
-            }
-            ++iter;
-        }
-    }
-
-    return *bestSoFar;
-}
-
 std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
                                                                  nsecs_t displayPeriod) const {
     auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e84091e..8ebbd74 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -239,10 +239,6 @@
         bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
     };
 
-    // Returns the refresh rate that fits best to the given layers.
-    const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
-            EXCLUDES(mLock);
-
     // Global state describing signals that affect refresh rate choice.
     struct GlobalSignals {
         // Whether the user touched the screen recently. Used to apply touch boost.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f6d712e..52bf483 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -89,9 +89,8 @@
                                                 timerSlack.count(), vsyncMoveThreshold.count());
 }
 
-const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) {
-    if (!useContentDetection) return "off";
-    return useContentDetectionV2 ? "V2" : "V1";
+const char* toContentDetectionString(bool useContentDetection) {
+    return useContentDetection ? "on" : "off";
 }
 
 } // namespace
@@ -119,14 +118,13 @@
 Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
       : Scheduler(configs, callback,
                   {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
-                   .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false),
-                   .useContentDetectionV2 =
-                           base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true)}) {}
+                   .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
+}
 
 Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
                      Options options)
       : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
-                  createLayerHistory(configs, options.useContentDetectionV2), options) {
+                  createLayerHistory(configs), options) {
     using namespace sysprop;
 
     const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
@@ -195,14 +193,10 @@
 }
 
 std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
-        const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) {
+        const scheduler::RefreshRateConfigs& configs) {
     if (!configs.canSwitch()) return nullptr;
 
-    if (useContentDetectionV2) {
-        return std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
-    }
-
-    return std::make_unique<scheduler::impl::LayerHistory>();
+    return std::make_unique<scheduler::LayerHistory>(configs);
 }
 
 std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
@@ -528,14 +522,6 @@
         // still need to be registered.
         mLayerHistory->registerLayer(layer, minFps, maxFps,
                                      scheduler::LayerHistory::LayerVoteType::Max);
-    } else if (!mOptions.useContentDetectionV2) {
-        // In V1 of content detection, all layers are registered as Heuristic (unless it's
-        // wallpaper).
-        const auto highFps =
-                layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps;
-
-        mLayerHistory->registerLayer(layer, minFps, highFps,
-                                     scheduler::LayerHistory::LayerVoteType::Heuristic);
     } else {
         if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
             // Running Wallpaper at Min is considered as part of content detection.
@@ -574,8 +560,6 @@
             return;
         }
         mFeatures.contentRequirements = summary;
-        mFeatures.contentDetectionV1 =
-                !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
 
         scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
         newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
@@ -682,8 +666,7 @@
     StringAppendF(&result, "+  Touch timer: %s\n",
                   mTouchTimer ? mTouchTimer->dump().c_str() : "off");
     StringAppendF(&result, "+  Content detection: %s %s\n\n",
-                  toContentDetectionString(mOptions.useContentDetection,
-                                           mOptions.useContentDetectionV2),
+                  toContentDetectionString(mOptions.useContentDetection),
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
 }
 
@@ -740,29 +723,6 @@
     const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
     const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
 
-    if (!mOptions.useContentDetectionV2) {
-        // As long as touch is active we want to be in performance mode.
-        if (touchActive) {
-            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
-        }
-
-        // If timer has expired as it means there is no new content on the screen.
-        if (idle) {
-            if (consideredSignals) consideredSignals->idle = true;
-            return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
-        }
-
-        // If content detection is off we choose performance as we don't know the content fps.
-        if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
-            // NOTE: V1 always calls this, but this is not a default behavior for V2.
-            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
-        }
-
-        // Content detection is on, find the appropriate refresh rate with minimal error
-        return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
-                .getConfigId();
-    }
-
     return mRefreshRateConfigs
             .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
                                 consideredSignals)
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 76e8f57..f16e1f9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -183,8 +183,6 @@
         bool supportKernelTimer;
         // Whether to use content detection at all.
         bool useContentDetection;
-        // Whether to use improved content detection.
-        bool useContentDetectionV2;
     };
 
     struct VsyncSchedule {
@@ -201,8 +199,7 @@
               std::unique_ptr<LayerHistory>, Options);
 
     static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
-    static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
-                                                            bool useContentDetectionV2);
+    static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&);
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -270,7 +267,6 @@
     std::mutex mFeatureStateLock;
 
     struct {
-        ContentDetectionState contentDetectionV1 = ContentDetectionState::Off;
         TimerState idleTimer = TimerState::Reset;
         TouchState touch = TouchState::Inactive;
         TimerState displayPowerTimer = TimerState::Expired;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 18f3745..7427e27 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -48,7 +48,6 @@
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
-        "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
         "MessageQueueTest.cpp",
         "PromiseTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 0fbe8ec..fbb4637 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,13 +14,10 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #undef LOG_TAG
 #define LOG_TAG "LayerHistoryTest"
 
+#include <Layer.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -39,32 +36,74 @@
 
 class LayerHistoryTest : public testing::Test {
 protected:
-    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
     static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
+    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::FREQUENT_LAYER_WINDOW_SIZE;
+    static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+    static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+            LayerInfo::RefreshRateHistory::HISTORY_DURATION;
 
     static constexpr float LO_FPS = 30.f;
-    static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
+    static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
 
     static constexpr float HI_FPS = 90.f;
-    static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
+    static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
 
     LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
 
     void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
 
-    impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
-    const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+    LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+    const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
 
     size_t layerCount() const { return mScheduler->layerHistorySize(); }
     size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
 
-    size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+    auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
         const auto& infos = history().mLayerInfos;
-        return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
                              [now](const auto& pair) { return pair.second->isFrequent(now); });
     }
 
+    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isAnimating(now); });
+    }
+
+    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);
+                return;
+            }
+        }
+    }
+
     auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+    auto createLayer(std::string name) {
+        return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+    }
+
+    void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+                               float desiredRefreshRate, int numFrames) {
+        const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+        LayerHistory::Summary summary;
+        for (int i = 0; i < numFrames; i++) {
+            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            time += framePeriod;
+
+            summary = history().summarize(time);
+        }
+
+        ASSERT_EQ(1, summary.size());
+        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+        ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+                << "Frame rate is " << frameRate;
+    }
 
     Hwc2::mock::Display mDisplay;
     RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
@@ -78,14 +117,10 @@
                                 HwcConfigIndexType(0)};
 
     mock::NoOpSchedulerCallback mSchedulerCallback;
-    static constexpr bool kUseContentDetectionV2 = false;
 
-    TestableScheduler* const mScheduler =
-            new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
+    TestableScheduler* const mScheduler = new TestableScheduler(mConfigs, mSchedulerCallback);
 
     TestableSurfaceFlinger mFlinger;
-
-    const nsecs_t mTime = systemTime();
 };
 
 namespace {
@@ -93,83 +128,259 @@
 TEST_F(LayerHistoryTest, oneLayer) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    // no layers are returned if no layers are active.
-    ASSERT_TRUE(history().summarize(mTime).empty());
+    const nsecs_t time = systemTime();
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(history().summarize(time).empty());
     EXPECT_EQ(0, activeLayerCount());
 
-    // no layers are returned if active layers have insufficient history.
+    // Max returned if active layers have insufficient history.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_TRUE(history().summarize(mTime).empty());
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 
-    // High FPS is returned once enough history has been recorded.
+    // Max is returned since we have enough history but there is no timestamp votes.
     for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_EQ(1, history().summarize(mTime).size());
-        EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
         EXPECT_EQ(1, activeLayerCount());
     }
 }
 
+TEST_F(LayerHistoryTest, oneInvisibleLayer) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    auto summary = history().summarize(time);
+    ASSERT_EQ(1, history().summarize(time).size());
+    // Layer is still considered inactive so we expect to get Min
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+    summary = history().summarize(time);
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+}
+
 TEST_F(LayerHistoryTest, explicitTimestamp) {
     const auto layer = createLayer();
     EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
     EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
 
-    nsecs_t time = mTime;
+    nsecs_t time = systemTime();
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
     }
 
-    ASSERT_EQ(1, history().summarize(mTime).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTest, oneLayerNoVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMinVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerMaxVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
+            .WillRepeatedly(
+                    Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive, but the vote stays
+    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);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive, but the vote stays
+    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,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
 TEST_F(LayerHistoryTest, multipleLayers) {
     auto layer1 = createLayer();
     auto layer2 = createLayer();
     auto layer3 = createLayer();
 
     EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
     EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
     EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
     EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
     EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-    nsecs_t time = mTime;
+
+    nsecs_t time = systemTime();
 
     EXPECT_EQ(3, layerCount());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
+    LayerHistory::Summary summary;
+
     // layer1 is active but infrequent.
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
@@ -177,26 +388,30 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
     // layer1 is still active but infrequent.
     history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
 
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
     EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer1 is no longer active.
     // layer2 is frequent and has low refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+    for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
@@ -210,26 +425,36 @@
 
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer3 becomes recently active.
     history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
 
     // layer1 expires.
     layer1.clear();
-    ASSERT_EQ(2, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
     EXPECT_EQ(2, layerCount());
     EXPECT_EQ(2, activeLayerCount());
     EXPECT_EQ(2, frequentLayerCount(time));
@@ -239,42 +464,290 @@
     for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
         history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer2 expires.
     layer2.clear();
-    ASSERT_TRUE(history().summarize(time).empty());
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 
     // layer3 becomes active and has high refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
         history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
         time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
     }
 
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
     EXPECT_EQ(1, layerCount());
     EXPECT_EQ(1, activeLayerCount());
     EXPECT_EQ(1, frequentLayerCount(time));
 
     // layer3 expires.
     layer3.clear();
-    ASSERT_TRUE(history().summarize(time).empty());
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
     EXPECT_EQ(0, layerCount());
     EXPECT_EQ(0, activeLayerCount());
     EXPECT_EQ(0, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTest, inactiveLayers) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    // the very first updates makes the layer frequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // advance the time for the previous frame to be inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+    // Now event if we post a quick few frame we should stay infrequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(0, frequentLayerCount(time));
+    }
+
+    // More quick frames will get us to frequent again
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += HI_FPS_PERIOD;
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
+    auto explicitVisiblelayer = createLayer();
+    auto explicitInvisiblelayer = createLayer();
+
+    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer.get(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    EXPECT_EQ(2, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // another update with the same cadence keep in infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // an update as animation will immediately vote for Max
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+        recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    }
+}
+
+TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+class LayerHistoryTestParameterized : public LayerHistoryTest,
+                                      public testing::WithParamInterface<std::chrono::nanoseconds> {
+};
+
+TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
+    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+    auto heuristicLayer = createLayer("HeuristicLayer");
+
+    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    auto infrequentLayer = createLayer("InfrequentLayer");
+    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    const nsecs_t startTime = systemTime();
+
+    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+    history().record(heuristicLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    nsecs_t time = startTime;
+    nsecs_t lastInfrequentUpdate = startTime;
+    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+    int infrequentLayerUpdates = 0;
+    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+        time += heuristicUpdateDelta.count();
+        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+            ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+                  totalInfrequentLayerUpdates);
+            lastInfrequentUpdate = time;
+            history().record(infrequentLayer.get(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
+            infrequentLayerUpdates++;
+        }
+
+        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+            ASSERT_NE(0, history().summarize(time).size());
+            ASSERT_GE(2, history().summarize(time).size());
+
+            bool max = false;
+            bool min = false;
+            float heuristic = 0;
+            for (const auto& layer : history().summarize(time)) {
+                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+                    heuristic = layer.desiredRefreshRate;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+                    max = true;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+                    min = true;
+                }
+            }
+
+            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+                EXPECT_FLOAT_EQ(24.0f, heuristic);
+                EXPECT_FALSE(max);
+                if (history().summarize(time).size() == 2) {
+                    EXPECT_TRUE(min);
+                }
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestParameterized,
+                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
 } // namespace
 } // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
deleted file mode 100644
index 7bfec9a..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTestV2"
-
-#include <Layer.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfoV2.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/MockLayer.h"
-#include "mock/MockSchedulerCallback.h"
-
-using testing::_;
-using testing::Return;
-
-namespace android::scheduler {
-
-class LayerHistoryTestV2 : public testing::Test {
-protected:
-    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
-    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
-    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
-    static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
-    static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
-            LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
-
-    static constexpr float LO_FPS = 30.f;
-    static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
-
-    static constexpr float HI_FPS = 90.f;
-    static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
-
-    LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
-
-    void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
-
-    impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
-    const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
-
-    size_t layerCount() const { return mScheduler->layerHistorySize(); }
-    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
-
-    auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
-        const auto& infos = history().mLayerInfos;
-        return std::count_if(infos.begin(),
-                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
-                             [now](const auto& pair) { return pair.second->isFrequent(now); });
-    }
-
-    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
-        const auto& infos = history().mLayerInfos;
-        return std::count_if(infos.begin(),
-                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
-                             [now](const auto& pair) { return pair.second->isAnimating(now); });
-    }
-
-    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);
-                return;
-            }
-        }
-    }
-
-    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
-    auto createLayer(std::string name) {
-        return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
-    }
-
-    void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
-                               float desiredRefreshRate, int numFrames) {
-        const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
-        impl::LayerHistoryV2::Summary summary;
-        for (int i = 0; i < numFrames; i++) {
-            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-            time += framePeriod;
-
-            summary = history().summarize(time);
-        }
-
-        ASSERT_EQ(1, summary.size());
-        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-        ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
-                << "Frame rate is " << frameRate;
-    }
-
-    Hwc2::mock::Display mDisplay;
-    RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
-                                         .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
-                                         .setConfigGroup(0)
-                                         .build(),
-                                 HWC2::Display::Config::Builder(mDisplay, 1)
-                                         .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
-                                         .setConfigGroup(0)
-                                         .build()},
-                                HwcConfigIndexType(0)};
-
-    mock::NoOpSchedulerCallback mSchedulerCallback;
-    static constexpr bool kUseContentDetectionV2 = true;
-
-    TestableScheduler* const mScheduler =
-            new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2);
-
-    TestableSurfaceFlinger mFlinger;
-};
-
-namespace {
-
-TEST_F(LayerHistoryTestV2, oneLayer) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    const nsecs_t time = systemTime();
-
-    // No layers returned if no layers are active.
-    EXPECT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-
-    // Max returned if active layers have insufficient history.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-        EXPECT_EQ(1, activeLayerCount());
-    }
-
-    // Max is returned since we have enough history but there is no timestamp votes.
-    for (int i = 0; i < 10; i++) {
-        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-        EXPECT_EQ(1, activeLayerCount());
-    }
-}
-
-TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-
-    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
-    auto summary = history().summarize(time);
-    ASSERT_EQ(1, history().summarize(time).size());
-    // Layer is still considered inactive so we expect to get Min
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
-
-    summary = history().summarize(time);
-    EXPECT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTestV2, explicitTimestamp) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-    ASSERT_TRUE(history().summarize(time).empty());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(
-                    Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive, but the vote stays
-    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);
-    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
-    auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-
-    nsecs_t time = systemTime();
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer became inactive, but the vote stays
-    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,
-              history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, multipleLayers) {
-    auto layer1 = createLayer();
-    auto layer2 = createLayer();
-    auto layer3 = createLayer();
-
-    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    EXPECT_EQ(3, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    impl::LayerHistoryV2::Summary summary;
-
-    // layer1 is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-        summary = history().summarize(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // layer2 is frequent and has high refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
-    }
-
-    // layer1 is still active but infrequent.
-    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
-    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer1 is no longer active.
-    // layer2 is frequent and has low refresh rate.
-    for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-        summary = history().summarize(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer2 still has low refresh rate.
-    // layer3 has high refresh rate but not enough history.
-    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
-        if (i % RATIO == 0) {
-            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        }
-
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
-    }
-
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer3 becomes recently active.
-    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    summary = history().summarize(time);
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer1 expires.
-    layer1.clear();
-    summary = history().summarize(time);
-    ASSERT_EQ(2, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
-    EXPECT_EQ(2, layerCount());
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-
-    // layer2 still has low refresh rate.
-    // layer3 becomes inactive.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += LO_FPS_PERIOD;
-        summary = history().summarize(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer2 expires.
-    layer2.clear();
-    summary = history().summarize(time);
-    EXPECT_TRUE(summary.empty());
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // layer3 becomes active and has high refresh rate.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
-        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-        summary = history().summarize(time);
-    }
-
-    ASSERT_EQ(1, summary.size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
-    EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-
-    // layer3 expires.
-    layer3.clear();
-    summary = history().summarize(time);
-    EXPECT_TRUE(summary.empty());
-    EXPECT_EQ(0, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, inactiveLayers) {
-    auto layer = createLayer();
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    // the very first updates makes the layer frequent
-    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-        EXPECT_EQ(1, layerCount());
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-        EXPECT_EQ(1, activeLayerCount());
-        EXPECT_EQ(1, frequentLayerCount(time));
-    }
-
-    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    EXPECT_EQ(1, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-
-    // advance the time for the previous frame to be inactive
-    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
-
-    // Now event if we post a quick few frame we should stay infrequent
-    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += HI_FPS_PERIOD;
-
-        EXPECT_EQ(1, layerCount());
-        ASSERT_EQ(1, history().summarize(time).size());
-        EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-        EXPECT_EQ(1, activeLayerCount());
-        EXPECT_EQ(0, frequentLayerCount(time));
-    }
-
-    // More quick frames will get us to frequent again
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    time += HI_FPS_PERIOD;
-
-    EXPECT_EQ(1, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
-    auto explicitVisiblelayer = createLayer();
-    auto explicitInvisiblelayer = createLayer();
-
-    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(
-                    Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
-    nsecs_t time = systemTime();
-
-    // Post a buffer to the layers to make them active
-    history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    history().record(explicitInvisiblelayer.get(), time, time,
-                     LayerHistory::LayerUpdateType::Buffer);
-
-    EXPECT_EQ(2, layerCount());
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
-              history().summarize(time)[0].vote);
-    EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
-    EXPECT_EQ(2, activeLayerCount());
-    EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
-    auto layer = createLayer();
-
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    EXPECT_EQ(1, layerCount());
-    EXPECT_EQ(0, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, animatingLayerCount(time));
-
-    // layer is active but infrequent.
-    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
-        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-    }
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, animatingLayerCount(time));
-
-    // another update with the same cadence keep in infrequent
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(0, animatingLayerCount(time));
-
-    // an update as animation will immediately vote for Max
-    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
-    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
-    ASSERT_EQ(1, history().summarize(time).size());
-    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
-    EXPECT_EQ(1, activeLayerCount());
-    EXPECT_EQ(0, frequentLayerCount(time));
-    EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
-        recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-    }
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-
-    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
-    const auto layer = createLayer();
-    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
-    nsecs_t time = systemTime();
-
-    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
-    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
-}
-
-class LayerHistoryTestV2Parameterized
-      : public LayerHistoryTestV2,
-        public testing::WithParamInterface<std::chrono::nanoseconds> {};
-
-TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
-    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
-    auto heuristicLayer = createLayer("HeuristicLayer");
-
-    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate()));
-
-    auto infrequentLayer = createLayer("InfrequentLayer");
-    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
-    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
-            .WillRepeatedly(Return(Layer::FrameRate()));
-
-    const nsecs_t startTime = systemTime();
-
-    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
-    history().record(heuristicLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
-    history().record(infrequentLayer.get(), startTime, startTime,
-                     LayerHistory::LayerUpdateType::Buffer);
-
-    nsecs_t time = startTime;
-    nsecs_t lastInfrequentUpdate = startTime;
-    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
-    int infrequentLayerUpdates = 0;
-    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
-        time += heuristicUpdateDelta.count();
-        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
-
-        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
-            ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
-                  totalInfrequentLayerUpdates);
-            lastInfrequentUpdate = time;
-            history().record(infrequentLayer.get(), time, time,
-                             LayerHistory::LayerUpdateType::Buffer);
-            infrequentLayerUpdates++;
-        }
-
-        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
-            ASSERT_NE(0, history().summarize(time).size());
-            ASSERT_GE(2, history().summarize(time).size());
-
-            bool max = false;
-            bool min = false;
-            float heuristic = 0;
-            for (const auto& layer : history().summarize(time)) {
-                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
-                    heuristic = layer.desiredRefreshRate;
-                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
-                    max = true;
-                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
-                    min = true;
-                }
-            }
-
-            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
-                EXPECT_FLOAT_EQ(24.0f, heuristic);
-                EXPECT_FALSE(max);
-                if (history().summarize(time).size() == 2) {
-                    EXPECT_TRUE(min);
-                }
-            }
-        }
-    }
-}
-
-INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
-                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 338e015..f2b7191 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -304,63 +304,6 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
-        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, Seamlessness::OnlySeamless,
-                 /*weight*/ 1.0f, /*focused*/ false}};
-    };
-
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
-    EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
-    EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-}
-
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
@@ -847,29 +790,6 @@
     }
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
-    auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(m60_90Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
-
-    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
-                                                LayerRequirement{.weight = 1.0f}};
-    auto& lr1 = layers[0];
-    auto& lr2 = layers[1];
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 60.0f;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 90.0f;
-    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
-
-    lr1.vote = LayerVoteType::Heuristic;
-    lr1.desiredRefreshRate = 90.0f;
-    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
-    lr2.desiredRefreshRate = 60.0f;
-    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
-}
-
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 647689b..fb02a33 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -68,8 +68,7 @@
         }
     } mExpectDisableVsync{mSchedulerCallback};
 
-    static constexpr bool kUseContentDetectionV2 = false;
-    TestableScheduler mScheduler{mConfigs, mSchedulerCallback, kUseContentDetectionV2};
+    TestableScheduler mScheduler{mConfigs, mSchedulerCallback};
 
     Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1b6e9ed..2192977 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -32,21 +32,16 @@
 
 class TestableScheduler : public Scheduler {
 public:
-    TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
-                      bool useContentDetectionV2)
+    TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
           : TestableScheduler(std::make_unique<mock::VsyncController>(),
-                              std::make_unique<mock::VSyncTracker>(), configs, callback,
-                              useContentDetectionV2) {}
+                              std::make_unique<mock::VSyncTracker>(), configs, callback) {}
 
     TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
                       std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
-                      const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
-                      bool useContentDetectionV2)
+                      const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback)
           : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
-                      callback, createLayerHistory(configs, useContentDetectionV2),
-                      {.supportKernelTimer = false,
-                       .useContentDetection = true,
-                       .useContentDetectionV2 = useContentDetectionV2}) {}
+                      callback, createLayerHistory(configs),
+                      {.supportKernelTimer = false, .useContentDetection = true}) {}
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -62,20 +57,11 @@
 
     bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
 
-    auto* mutableLayerHistory() {
-        LOG_ALWAYS_FATAL_IF(mOptions.useContentDetectionV2);
-        return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
-    }
-
-    auto* mutableLayerHistoryV2() {
-        LOG_ALWAYS_FATAL_IF(!mOptions.useContentDetectionV2);
-        return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
-    }
+    auto* mutableLayerHistory() { return mLayerHistory.get(); }
 
     size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
         if (!mLayerHistory) return 0;
-        return mOptions.useContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size()
-                                              : mutableLayerHistory()->mLayerInfos.size();
+        return mutableLayerHistory()->mLayerInfos.size();
     }
 
     void replaceTouchTimer(int64_t millis) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index eba3f8e..8a0276a 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -227,10 +227,8 @@
                 mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
         mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
-        constexpr bool kUseContentDetectionV2 = false;
         mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
-                                           *mFlinger->mRefreshRateConfigs, *(callback ?: this),
-                                           kUseContentDetectionV2);
+                                           *mFlinger->mRefreshRateConfigs, *(callback ?: this));
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));