Add refresh rate stats to TimeStats.

Bug: 122905821
Test: libsurfaceflinger_test
Test: dumpsys SurfaceFlinger --timestats -dump --proto
Change-Id: I99b38497f054f86bebba0813134f1c1eaf632b47
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 8d3776b..78c6e74 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,6 +32,8 @@
 
 namespace android {
 
+namespace impl {
+
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
@@ -450,6 +452,15 @@
     mPowerTime.powerMode = powerMode;
 }
 
+void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mTimeStats.refreshRateStats.count(fps)) {
+        mTimeStats.refreshRateStats[fps] += duration;
+    } else {
+        mTimeStats.refreshRateStats.insert({fps, duration});
+    }
+}
+
 void TimeStats::flushAvailableGlobalRecordsToStatsLocked() {
     ATRACE_CALL();
 
@@ -547,6 +558,7 @@
     mTimeStats.clientCompositionFrames = 0;
     mTimeStats.displayOnTime = 0;
     mTimeStats.presentToPresent.hist.clear();
+    mTimeStats.refreshRateStats.clear();
     mPowerTime.prevTime = systemTime();
     mGlobalRecord.prevPresentTime = 0;
     mGlobalRecord.presentFences.clear();
@@ -580,4 +592,6 @@
     }
 }
 
+} // namespace impl
+
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index e8fbcab..d8c0786 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -36,6 +36,40 @@
 namespace android {
 
 class TimeStats {
+public:
+    virtual ~TimeStats() = default;
+
+    virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
+    virtual bool isEnabled() = 0;
+
+    virtual void incrementTotalFrames() = 0;
+    virtual void incrementMissedFrames() = 0;
+    virtual void incrementClientCompositionFrames() = 0;
+
+    virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+                             nsecs_t postTime) = 0;
+    virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
+    virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+    virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+    virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+                                 const std::shared_ptr<FenceTime>& acquireFence) = 0;
+    virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
+    virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+                                 const std::shared_ptr<FenceTime>& presentFence) = 0;
+    // Clean up the layer record
+    virtual void onDestroy(int32_t layerID) = 0;
+    // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
+    virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+
+    virtual void setPowerMode(int32_t powerMode) = 0;
+    // Source of truth is RefrehRateStats.
+    virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
+    virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
+};
+
+namespace impl {
+
+class TimeStats : public android::TimeStats {
     struct FrameTime {
         uint64_t frameNumber = 0;
         nsecs_t postTime = 0;
@@ -75,32 +109,33 @@
 
 public:
     TimeStats() = default;
-    ~TimeStats() = default;
 
-    void parseArgs(bool asProto, const Vector<String16>& args, std::string& result);
-    bool isEnabled();
+    void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
+    bool isEnabled() override;
 
-    void incrementTotalFrames();
-    void incrementMissedFrames();
-    void incrementClientCompositionFrames();
+    void incrementTotalFrames() override;
+    void incrementMissedFrames() override;
+    void incrementClientCompositionFrames() override;
 
     void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
-                     nsecs_t postTime);
-    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime);
-    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime);
-    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime);
+                     nsecs_t postTime) override;
+    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
+    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
+    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
     void setAcquireFence(int32_t layerID, uint64_t frameNumber,
-                         const std::shared_ptr<FenceTime>& acquireFence);
-    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
+                         const std::shared_ptr<FenceTime>& acquireFence) override;
+    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
     void setPresentFence(int32_t layerID, uint64_t frameNumber,
-                         const std::shared_ptr<FenceTime>& presentFence);
+                         const std::shared_ptr<FenceTime>& presentFence) override;
     // Clean up the layer record
-    void onDestroy(int32_t layerID);
+    void onDestroy(int32_t layerID) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
+    void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
 
-    void setPowerMode(int32_t powerMode);
-    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
+    void setPowerMode(int32_t powerMode) override;
+    // Source of truth is RefrehRateStats.
+    void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
+    void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;
 
     // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
     // static const size_t MAX_NUM_LAYER_RECORDS = 200;
@@ -126,4 +161,6 @@
     GlobalRecord mGlobalRecord;
 };
 
+} // namespace impl
+
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 75ce4be..16d2da0 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -103,6 +103,11 @@
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
     StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+    StringAppendF(&result, "displayConfigStats is as below:\n");
+    for (const auto& [fps, duration] : refreshRateStats) {
+        StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration));
+    }
+    result.back() = '\n';
     StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
     StringAppendF(&result, "presentToPresent histogram is as below:\n");
     result.append(presentToPresent.toString());
@@ -141,6 +146,13 @@
     globalProto.set_missed_frames(missedFrames);
     globalProto.set_client_composition_frames(clientCompositionFrames);
     globalProto.set_display_on_time(displayOnTime);
+    for (const auto& ele : refreshRateStats) {
+        SFTimeStatsDisplayConfigBucketProto* configBucketProto =
+                globalProto.add_display_config_stats();
+        SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config();
+        configProto->set_fps(ele.first);
+        configBucketProto->set_duration_millis(ns2ms(ele.second));
+    }
     for (const auto& histEle : presentToPresent.hist) {
         SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present();
         histProto->set_time_millis(histEle.first);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5f40a1a..f2ac7ff 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -16,6 +16,7 @@
 #pragma once
 
 #include <timestatsproto/TimeStatsProtoHeader.h>
+#include <utils/Timers.h>
 
 #include <optional>
 #include <string>
@@ -61,6 +62,7 @@
         int64_t displayOnTime = 0;
         Histogram presentToPresent;
         std::unordered_map<std::string, TimeStatsLayer> stats;
+        std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
 
         std::string toString(std::optional<uint32_t> maxLayers) const;
         SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 377612a..0dacbeb 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
 // changes to these messages, and keep google3 side proto messages in sync if
 // the end to end pipeline needs to be updated.
 
-// Next tag: 9
+// Next tag: 10
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -39,6 +39,8 @@
   optional int32 client_composition_frames = 5;
   // Primary display on time in milliseconds.
   optional int64 display_on_time = 7;
+  // Stats per display configuration.
+  repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
   // Present to present histogram.
   repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
   // Stats per layer. Apps could have multiple layers.
@@ -80,3 +82,18 @@
   // Number of frames in the bucket.
   optional int32 frame_count = 2;
 }
+
+// Next tag: 3
+message SFTimeStatsDisplayConfigBucketProto {
+    // Metadata desribing a display config.
+    optional SFTimeStatsDisplayConfigProto config = 1;
+    // Duration in milliseconds for how long the display was in this
+    // configuration.
+    optional int64 duration_millis = 2;
+}
+
+// Next tag: 2
+message SFTimeStatsDisplayConfigProto {
+    // Frames per second, rounded to the nearest integer.
+    optional int32 fps = 1;
+}