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/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index bfe5da5..026f7c7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -47,6 +47,8 @@
         int configId;
         // Human readable name of the refresh rate.
         std::string name;
+        // Refresh rate in frames per second, rounded to the nearest integer.
+        uint32_t fps = 0;
     };
 
     // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
@@ -63,7 +65,7 @@
     void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
         // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
         mRefreshRates.push_back(
-                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff"});
+                RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
 
         if (configs.size() < 1) {
             ALOGE("Device does not have valid configs. Config size is 0.");
@@ -86,10 +88,11 @@
         nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
-            mRefreshRates.push_back(RefreshRate{RefreshRateType::DEFAULT,
-                                                configIdToVsyncPeriod[0].first,
-                                                base::StringPrintf("%2.ffps", fps)});
+            mRefreshRates.push_back(
+                    RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first,
+                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
         }
+
         if (configs.size() < 2) {
             return;
         }
@@ -99,9 +102,9 @@
         vsyncPeriod = configIdToVsyncPeriod[1].second;
         if (vsyncPeriod != 0) {
             const float fps = 1e9 / vsyncPeriod;
-            mRefreshRates.push_back(RefreshRate{RefreshRateType::PERFORMANCE,
-                                                configIdToVsyncPeriod[1].first,
-                                                base::StringPrintf("%2.ffps", fps)});
+            mRefreshRates.push_back(
+                    RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first,
+                                base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)});
         }
     }
 
@@ -109,4 +112,4 @@
 };
 
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 7e22232..dcb2988 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -20,6 +20,7 @@
 
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/SchedulerUtils.h"
+#include "TimeStats/TimeStats.h"
 
 #include "android-base/stringprintf.h"
 #include "utils/Timers.h"
@@ -41,8 +42,10 @@
 
 public:
     explicit RefreshRateStats(
-            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs)
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+            const std::shared_ptr<TimeStats>& timeStats)
           : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)),
+            mTimeStats(timeStats),
             mPreviousRecordedTime(systemTime()) {}
     ~RefreshRateStats() = default;
 
@@ -116,10 +119,16 @@
     // this method was called.
     void flushTimeForMode(int mode) {
         nsecs_t currentTime = systemTime();
-        int64_t timeElapsedMs = ns2ms(currentTime - mPreviousRecordedTime);
+        nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
+        int64_t timeElapsedMs = ns2ms(timeElapsed);
         mPreviousRecordedTime = currentTime;
 
         mConfigModesTotalTime[mode] += timeElapsedMs;
+        for (const auto& config : mRefreshRateConfigs->getRefreshRates()) {
+            if (config.configId == mode) {
+                mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+            }
+        }
     }
 
     // Formats the time in milliseconds into easy to read format.
@@ -136,6 +145,9 @@
     // Keeps information about refresh rate configs that device has.
     std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
 
+    // Aggregate refresh rate statistics for telemetry.
+    std::shared_ptr<TimeStats> mTimeStats;
+
     int64_t mCurrentConfigMode = 0;
     int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
 
@@ -145,4 +157,4 @@
 };
 
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2cf2cd8..1626555 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -740,8 +740,10 @@
             mVsyncModulator.setPhaseOffsets(early, gl, late);
             setRefreshRateTo(90.f /* fps */);
         });
-        mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(
-                getHwComposer().getConfigs(*display->getId()));
+        mRefreshRateStats =
+                std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs(
+                                                                      *display->getId()),
+                                                              mTimeStats);
     }
 
     ALOGV("Done initializing");
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a48e811..9aaf6de 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1000,7 +1000,7 @@
     std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
     SurfaceTracing mTracing;
     LayerStats mLayerStats;
-    std::unique_ptr<TimeStats> mTimeStats;
+    std::shared_ptr<TimeStats> mTimeStats;
     bool mUseHwcVirtualDisplays = false;
     std::atomic<uint32_t> mFrameMissedCount{0};
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 773a5d1..26d2c21 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -128,8 +128,8 @@
             return new ColorLayer(args);
         }
 
-        std::unique_ptr<TimeStats> createTimeStats() override {
-            return std::make_unique<TimeStats>();
+        std::shared_ptr<TimeStats> createTimeStats() override {
+            return std::make_shared<android::impl::TimeStats>();
         }
     };
     static Factory factory;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index ac99e2a..fc1d0f8 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -93,7 +93,7 @@
     virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
 
-    virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+    virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
 
 protected:
     ~Factory() = default;
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;
+}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index b2bcb45..b1d45f39 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -58,6 +58,7 @@
         "mock/MockMessageQueue.cpp",
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
+        "mock/MockTimeStats.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 0384d9d..3d887ea 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -23,8 +23,10 @@
 
 #include "Scheduler/RefreshRateStats.h"
 #include "mock/DisplayHardware/MockDisplay.h"
+#include "mock/MockTimeStats.h"
 
 using namespace std::chrono_literals;
+using testing::_;
 
 namespace android {
 namespace scheduler {
@@ -42,6 +44,7 @@
     void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs);
 
     std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+    std::shared_ptr<android::mock::TimeStats> mTimeStats;
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -57,7 +60,8 @@
 }
 
 void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) {
-    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs);
+    mTimeStats = std::make_shared<android::mock::TimeStats>();
+    mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats);
 }
 
 namespace {
@@ -82,6 +86,9 @@
 
     init(configs);
 
+    EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(4);
+    EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(2);
+
     std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
     ASSERT_EQ(2, times.size());
     ASSERT_EQ(0, times["ScreenOff"]);
@@ -136,6 +143,10 @@
 
     init(configs);
 
+    EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(6);
+    EXPECT_CALL(*mTimeStats, recordRefreshRate(60, _)).Times(4);
+    EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(4);
+
     std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
     ASSERT_EQ(3, times.size());
     ASSERT_EQ(0, times["ScreenOff"]);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a62c40..e639b4d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -144,9 +144,9 @@
         return nullptr;
     }
 
-    std::unique_ptr<TimeStats> createTimeStats() override {
+    std::shared_ptr<TimeStats> createTimeStats() override {
         // TODO: Use test-fixture controlled factory
-        return std::make_unique<TimeStats>();
+        return std::make_shared<android::impl::TimeStats>();
     }
 
     using CreateBufferQueueFunction =
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 0f95cf9..f35758d 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -17,6 +17,7 @@
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <log/log.h>
@@ -24,6 +25,7 @@
 #include <utils/Vector.h>
 
 #include <random>
+#include <unordered_set>
 
 #include "TimeStats/TimeStats.h"
 
@@ -35,6 +37,10 @@
 namespace android {
 namespace {
 
+using testing::Contains;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
 // clang-format off
 #define FMT_PROTO          true
 #define FMT_STRING         false
@@ -127,7 +133,7 @@
     }
 
     std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
 };
 
 std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -371,6 +377,63 @@
     }
 }
 
+TEST_F(TimeStatsTest, recordRefreshRateNewConfigs) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    uint32_t fpsOne = 30;
+    uint32_t fpsTwo = 90;
+    uint64_t millisOne = 5000;
+    uint64_t millisTwo = 7000;
+
+    mTimeStats->recordRefreshRate(fpsOne, ms2ns(millisOne));
+    mTimeStats->recordRefreshRate(fpsTwo, ms2ns(millisTwo));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    SFTimeStatsDisplayConfigBucketProto expectedBucketOne;
+    SFTimeStatsDisplayConfigProto* expectedConfigOne = expectedBucketOne.mutable_config();
+    expectedConfigOne->set_fps(fpsOne);
+    expectedBucketOne.set_duration_millis(millisOne);
+
+    SFTimeStatsDisplayConfigBucketProto expectedBucketTwo;
+    SFTimeStatsDisplayConfigProto* expectedConfigTwo = expectedBucketTwo.mutable_config();
+    expectedConfigTwo->set_fps(fpsTwo);
+    expectedBucketTwo.set_duration_millis(millisTwo);
+
+    EXPECT_THAT(globalProto.display_config_stats(), SizeIs(2));
+
+    std::unordered_set<uint32_t> seen_fps;
+    for (const auto& bucket : globalProto.display_config_stats()) {
+        seen_fps.emplace(bucket.config().fps());
+        if (fpsOne == bucket.config().fps()) {
+            EXPECT_EQ(millisOne, bucket.duration_millis());
+        } else if (fpsTwo == bucket.config().fps()) {
+            EXPECT_EQ(millisTwo, bucket.duration_millis());
+        } else {
+            FAIL() << "Unknown fps: " << bucket.config().fps();
+        }
+    }
+    EXPECT_THAT(seen_fps, UnorderedElementsAre(fpsOne, fpsTwo));
+}
+
+TEST_F(TimeStatsTest, recordRefreshRateUpdatesConfig) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    uint32_t fps = 30;
+    uint64_t millisOne = 5000;
+    uint64_t millisTwo = 7000;
+
+    mTimeStats->recordRefreshRate(fps, ms2ns(millisOne));
+    mTimeStats->recordRefreshRate(fps, ms2ns(millisTwo));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+    EXPECT_THAT(globalProto.display_config_stats(), SizeIs(1));
+    EXPECT_EQ(fps, globalProto.display_config_stats().Get(0).config().fps());
+    EXPECT_EQ(millisOne + millisTwo, globalProto.display_config_stats().Get(0).duration_millis());
+}
+
 TEST_F(TimeStatsTest, canRemoveTimeRecord) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -394,7 +457,7 @@
     uint64_t frameNumber = 1;
     nsecs_t ts = 1000000;
     insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
-    for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+    for (size_t i = 0; i < impl::TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
         frameNumber++;
         ts += 1000000;
         insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
new file mode 100644
index 0000000..d686939
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2019 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.
+ */
+
+#include "mock/MockTimeStats.h"
+
+namespace android {
+namespace mock {
+
+// Explicit default instantiation is recommended.
+TimeStats::TimeStats() = default;
+TimeStats::~TimeStats() = default;
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
new file mode 100644
index 0000000..08fdb9d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 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 <gmock/gmock.h>
+
+#include "TimeStats/TimeStats.h"
+
+namespace android {
+namespace mock {
+
+class TimeStats : public android::TimeStats {
+public:
+    TimeStats();
+    ~TimeStats() override;
+
+    MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
+    MOCK_METHOD0(isEnabled, bool());
+    MOCK_METHOD0(incrementTotalFrames, void());
+    MOCK_METHOD0(incrementMissedFrames, void());
+    MOCK_METHOD0(incrementClientCompositionFrames, void());
+    MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
+    MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
+    MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
+    MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
+    MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD1(onDestroy, void(int32_t));
+    MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
+    MOCK_METHOD1(setPowerMode, void(int32_t));
+    MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t));
+    MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
+};
+
+} // namespace mock
+} // namespace android