SF: Suppress frame rate when small area updating

The small area is defined as the area that user is hard to obvious the
content updating such as small icon animtion or progress bar.

When content detect frame rate feature is enabled, and no explict
refresh vote is set to the layer, it will heuristically calculate the
predict frame rate by counting the average frame time of recent frames.

This CL will enhance the contect detection by checking the small dirty
frames and supress the predict frame rate that could prevent the app
always running at max frame rate cause unexcpected battery consumption.

This will cover 2 use cases:
1. When a layer contains a textureview, it will enter the heuristic
   calcuation, and this would skip the small dirty frames and supress
   the calculated frame rate not always to be max.

2. When an app has the separated video surface and ui surface, and the
   video had set an explicit rate by the content, this will ignore the
   heristic calculation for ui layer when it detected the small dirty
   frames, so the final refresh rate selection could mainly rely on the
   weight of the video layer.

Bug: 281720315
Test: atest LayerHistoryTest
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:65705849c4f6339ade986bd0745ea5470ba76ebb)
Merged-In: I9731de8adf8e68b72326b195fa720d51e02bae74
Change-Id: I9731de8adf8e68b72326b195fa720d51e02bae74
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 50f24a7..8f785a0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -3212,6 +3212,14 @@
     }
 
     mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
+
+    // If the layer had been updated a TextureView, this would make sure the present time could be
+    // same to TextureView update when it's a small dirty, and get the correct heuristic rate.
+    if (mFlinger->mScheduler->supportSmallDirtyDetection()) {
+        if (mDrawingState.useVsyncIdForRefreshRateSelection) {
+            mUsedVsyncIdForRefreshRateSelection = true;
+        }
+    }
     return true;
 }
 
@@ -3234,10 +3242,38 @@
                             mDrawingState.latchedVsyncId);
             if (prediction.has_value()) {
                 ATRACE_FORMAT_INSTANT("predictedPresentTime");
+                mMaxTimeForUseVsyncId = prediction->presentTime +
+                        scheduler::LayerHistory::kMaxPeriodForHistory.count();
                 return prediction->presentTime;
             }
         }
 
+        if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+            return static_cast<nsecs_t>(0);
+        }
+
+        // If the layer is not an application and didn't set an explicit rate or desiredPresentTime,
+        // return "0" to tell the layer history that it will use the max refresh rate without
+        // calculating the adaptive rate.
+        if (mWindowType != WindowInfo::Type::APPLICATION &&
+            mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+            return static_cast<nsecs_t>(0);
+        }
+
+        // Return the valid present time only when the layer potentially updated a TextureView so
+        // LayerHistory could heuristically calculate the rate if the UI is continually updating.
+        if (mUsedVsyncIdForRefreshRateSelection) {
+            const auto prediction =
+                    mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                            mDrawingState.latchedVsyncId);
+            if (prediction.has_value()) {
+                if (mMaxTimeForUseVsyncId >= prediction->presentTime) {
+                    return prediction->presentTime;
+                }
+                mUsedVsyncIdForRefreshRateSelection = false;
+            }
+        }
+
         return static_cast<nsecs_t>(0);
     }();
 
@@ -3297,6 +3333,7 @@
     mDrawingState.surfaceDamageRegion = surfaceDamage;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
+    setIsSmallDirty();
     return true;
 }
 
@@ -4335,6 +4372,25 @@
     mLastLatchTime = latchTime;
 }
 
+void Layer::setIsSmallDirty() {
+    if (!mFlinger->mScheduler->supportSmallDirtyDetection()) {
+        return;
+    }
+
+    if (mWindowType != WindowInfo::Type::APPLICATION &&
+        mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+        return;
+    }
+    Rect bounds = mDrawingState.surfaceDamageRegion.getBounds();
+    if (!bounds.isValid()) {
+        return;
+    }
+
+    // If the damage region is a small dirty, this could give the hint for the layer history that
+    // it could suppress the heuristic rate when calculating.
+    mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(bounds.getWidth() * bounds.getHeight());
+}
+
 } // namespace android
 
 #if defined(__gl_h_)