SF: tune infrequent layers for animations

Classify a layer as frequent or not based the last buffer pattern.
When unknown (e.g. the first buffer after inactivity), return
the last known state of the layer. This change helps to correctly classify
animating layers that were inactive and now posts the first buffer.

Bug: 264952266
Test: atest libsurfaceflinger_unittest
Change-Id: Ie6a1a21c00f7bb58468da033e98d5e35eb0c4c8d
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 7247e4b..9e5b4bd 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -76,12 +76,43 @@
 
 bool LayerInfo::isFrequent(nsecs_t now) const {
     using fps_approx_ops::operator>=;
-    // If we know nothing about this layer we consider it as frequent as it might be the start
-    // of an animation.
+    // If we know nothing about this layer (e.g. after touch event),
+    // we consider it as frequent as it might be the start of an animation.
     if (mFrameTimes.size() < kFrequentLayerWindowSize) {
         return true;
     }
-    return getFps(now) >= kMinFpsForFrequentLayer;
+
+    // Non-active layers are also infrequent
+    if (mLastUpdatedTime < getActiveLayerThreshold(now)) {
+        return false;
+    }
+
+    // We check whether we can classify this layer as frequent or infrequent:
+    //  - frequent: a layer posted kFrequentLayerWindowSize within
+    //              kMaxPeriodForFrequentLayerNs of each other.
+    // -  infrequent: a layer posted kFrequentLayerWindowSize with longer
+    //                gaps than kFrequentLayerWindowSize.
+    // If we can't determine the layer classification yet, we return the last
+    // classification.
+    bool isFrequent = true;
+    bool isInfrequent = true;
+    const auto n = mFrameTimes.size() - 1;
+    for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) {
+        if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime <
+            kMaxPeriodForFrequentLayerNs.count()) {
+            isInfrequent = false;
+        } else {
+            isFrequent = false;
+        }
+    }
+
+    if (isFrequent || isInfrequent) {
+        return isFrequent;
+    }
+
+    // If we can't determine whether the layer is frequent or not, we return
+    // the last known classification.
+    return !mLastRefreshRate.infrequent;
 }
 
 Fps LayerInfo::getFps(nsecs_t now) const {
@@ -231,13 +262,13 @@
 
     if (isAnimating(now)) {
         ALOGV("%s is animating", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
+        mLastRefreshRate.animating = true;
         return {LayerHistory::LayerVoteType::Max, Fps()};
     }
 
     if (!isFrequent(now)) {
         ALOGV("%s is infrequent", mName.c_str());
-        mLastRefreshRate.animatingOrInfrequent = true;
+        mLastRefreshRate.infrequent = true;
         // Infrequent layers vote for mininal refresh rate for
         // battery saving purposes and also to prevent b/135718869.
         return {LayerHistory::LayerVoteType::Min, Fps()};
@@ -246,7 +277,7 @@
     // 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) {
+    if (mLastRefreshRate.animating || mLastRefreshRate.infrequent) {
         clearHistory(now);
     }