psh_utils: Internally cache power stats

To avoid frequent (expensive) collection of power stats
allow the use of cached values with a certain time
tolerance.

Flag: com.android.media.audioserver.power_stats
Test: atest powerstats_collector_tests
Test: atest audio_powerstatscollector_benchmark
Bug: 350114693
Change-Id: I952950e5373e3c4b8ebc765bf6fb2cf8def0962e
diff --git a/media/psh_utils/PowerStatsCollector.cpp b/media/psh_utils/PowerStatsCollector.cpp
index 7bee3f8..6e02993 100644
--- a/media/psh_utils/PowerStatsCollector.cpp
+++ b/media/psh_utils/PowerStatsCollector.cpp
@@ -32,12 +32,43 @@
     return psc;
 }
 
-std::shared_ptr<PowerStats> PowerStatsCollector::getStats() const {
+std::shared_ptr<const PowerStats> PowerStatsCollector::getStats(int64_t toleranceNs) {
+    // Check if there is a cached PowerStats result available.
+    // As toleranceNs may be different between callers, it may be that some callers
+    // are blocked on mMutexExclusiveFill for a new stats result, while other callers
+    // may find the current cached result acceptable (within toleranceNs).
+    if (toleranceNs > 0) {
+        auto result = checkLastStats(toleranceNs);
+        if (result) return result;
+    }
+
+    // Take the mMutexExclusiveFill to ensure only one thread is filling.
+    std::lock_guard lg1(mMutexExclusiveFill);
+    // As obtaining a new PowerStats snapshot might take some time,
+    // check again to see if another waiting thread filled the cached result for us.
+    if (toleranceNs > 0) {
+        auto result = checkLastStats(toleranceNs);
+        if (result) return result;
+    }
     auto result = std::make_shared<PowerStats>();
     (void)fill(result.get());
+    std::lock_guard lg2(mMutex);
+    mLastFetchNs = systemTime(SYSTEM_TIME_BOOTTIME);
+    mLastFetchStats = result;
     return result;
 }
 
+std::shared_ptr<const PowerStats> PowerStatsCollector::checkLastStats(int64_t toleranceNs) const {
+    if (toleranceNs > 0) {
+        // see if we can return an old result.
+        std::lock_guard lg(mMutex);
+        if (mLastFetchStats && systemTime(SYSTEM_TIME_BOOTTIME) - mLastFetchNs < toleranceNs) {
+            return mLastFetchStats;
+        }
+    }
+    return {};
+}
+
 void PowerStatsCollector::addProvider(std::unique_ptr<PowerStatsProvider>&& powerStatsProvider) {
     mPowerStatsProviders.emplace_back(std::move(powerStatsProvider));
 }