Add deadline histograms into timestats

* SF deadline histogram
* Present time jitter histogram
* app deadline histogram

Histograms are only incremented if the associated (actual - predicted)
deltas are positive and jank is reported by the timeline.

Bug: 178003475
Test: builds, boots
Change-Id: Ic348cf9f2ed8d4e30bd0b26835cc0abf22cedfb2
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3f368c3..eb0c724 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -399,10 +399,10 @@
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
-void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate) {
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
+                             nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) {
     std::scoped_lock lock(mMutex);
 
-    const Fps renderRate = mRenderRate ? *mRenderRate : refreshRate;
     if (mPresentState != PresentState::Presented) {
         // No need to update dropped buffers
         return;
@@ -421,7 +421,10 @@
         mJankType = JankType::Unknown;
         mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
         mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
-        mTimeStats->incrementJankyFrames(refreshRate, renderRate, mOwnerUid, mLayerName, mJankType);
+        const constexpr nsecs_t kAppDeadlineDelta = -1;
+        mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName,
+                                          mJankType, displayDeadlineDelta, displayPresentDelta,
+                                          kAppDeadlineDelta});
         return;
     }
 
@@ -493,7 +496,8 @@
             }
         }
     }
-    mTimeStats->incrementJankyFrames(refreshRate, renderRate, mOwnerUid, mLayerName, mJankType);
+    mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, mJankType,
+                                      displayDeadlineDelta, displayPresentDelta, deadlineDelta});
 }
 
 /**
@@ -750,6 +754,9 @@
     // Delta between the expected present and the actual present
     const nsecs_t presentDelta =
             mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+    const nsecs_t deadlineDelta =
+            mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime;
+
     // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
     // was a prediction error or not.
     nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs();
@@ -825,7 +832,7 @@
         }
     }
     for (auto& surfaceFrame : mSurfaceFrames) {
-        surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate);
+        surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync);
     }
 }
 
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 3ddd900..4739106 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -183,7 +183,10 @@
     // Used for dumping all timestamps relative to the oldest, making it easy to read.
     nsecs_t getBaseTime() const;
     // Sets the actual present time, appropriate metadata and classifies the jank.
-    void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate);
+    // displayRefreshRate, displayDeadlineDelta, and displayPresentDelta are propagated from the
+    // display frame.
+    void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
+                   nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
     // All the timestamps are dumped relative to the baseTime
     void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
     // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c3f3671..b93f30e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -147,17 +147,17 @@
         mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing
         mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket);
         std::string sfDeadlineMissedBytes =
-                histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+                histogramToProtoByteString(globalSlice.second.displayDeadlineDeltas.hist,
                                            mMaxPulledHistogramBuckets);
-        mStatsDelegate
-                ->statsEventWriteByteArray(event, (const uint8_t*)sfDeadlineMissedBytes.c_str(),
-                                           sfDeadlineMissedBytes.size()); // sf_deadline_misses
+        mStatsDelegate->statsEventWriteByteArray(event,
+                                                 (const uint8_t*)sfDeadlineMissedBytes.c_str(),
+                                                 sfDeadlineMissedBytes.size());
         std::string sfPredictionErrorBytes =
-                histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+                histogramToProtoByteString(globalSlice.second.displayPresentDeltas.hist,
                                            mMaxPulledHistogramBuckets);
-        mStatsDelegate
-                ->statsEventWriteByteArray(event, (const uint8_t*)sfPredictionErrorBytes.c_str(),
-                                           sfPredictionErrorBytes.size()); // sf_prediction_errors
+        mStatsDelegate->statsEventWriteByteArray(event,
+                                                 (const uint8_t*)sfPredictionErrorBytes.c_str(),
+                                                 sfPredictionErrorBytes.size());
         mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket);
         mStatsDelegate->statsEventBuild(event);
     }
@@ -170,7 +170,7 @@
 AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
+    std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
     uint32_t numLayers = 0;
     for (const auto& globalSlice : mTimeStats.stats) {
         numLayers += globalSlice.second.stats.size();
@@ -178,8 +178,8 @@
 
     dumpStats.reserve(numLayers);
 
-    for (const auto& globalSlice : mTimeStats.stats) {
-        for (const auto& layerSlice : globalSlice.second.stats) {
+    for (auto& globalSlice : mTimeStats.stats) {
+        for (auto& layerSlice : globalSlice.second.stats) {
             dumpStats.push_back(&layerSlice.second);
         }
     }
@@ -194,7 +194,7 @@
         dumpStats.resize(mMaxPulledLayers);
     }
 
-    for (const auto& layer : dumpStats) {
+    for (auto& layer : dumpStats) {
         AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
         mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
         mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
@@ -234,11 +234,11 @@
         mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
                                                  frameRateVoteBytes.size()); // set_frame_rate_vote
         std::string appDeadlineMissedBytes =
-                histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+                histogramToProtoByteString(layer->deltas["appDeadlineDeltas"].hist,
                                            mMaxPulledHistogramBuckets);
-        mStatsDelegate
-                ->statsEventWriteByteArray(event, (const uint8_t*)appDeadlineMissedBytes.c_str(),
-                                           appDeadlineMissedBytes.size()); // app_deadline_misses
+        mStatsDelegate->statsEventWriteByteArray(event,
+                                                 (const uint8_t*)appDeadlineMissedBytes.c_str(),
+                                                 appDeadlineMissedBytes.size());
 
         mStatsDelegate->statsEventBuild(event);
     }
@@ -772,13 +772,14 @@
     flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
 }
 
+static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+        JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+        JankType::DisplayHAL;
+
 template <class T>
 static void updateJankPayload(T& t, int32_t reasons) {
     t.jankPayload.totalFrames++;
 
-    static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
-            JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
-            JankType::DisplayHAL;
     if (reasons & kValidJankyReason) {
         t.jankPayload.totalJankyFrames++;
         if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
@@ -796,8 +797,7 @@
     }
 }
 
-void TimeStats::incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
-                                     const std::string& layerName, int32_t reasons) {
+void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -816,9 +816,11 @@
 
     static const std::string kDefaultLayerName = "none";
 
-    const int32_t refreshRateBucket = clampToSmallestBucket(refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+    const int32_t refreshRateBucket =
+            clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
     const int32_t renderRateBucket =
-            clampToSmallestBucket(renderRate ? *renderRate : refreshRate, RENDER_RATE_BUCKET_WIDTH);
+            clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+                                  RENDER_RATE_BUCKET_WIDTH);
     const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
 
     if (!mTimeStats.stats.count(timelineKey)) {
@@ -827,19 +829,29 @@
 
     TimeStatsHelper::TimelineStats& timelineStats = mTimeStats.stats[timelineKey];
 
-    updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, reasons);
+    updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons);
 
-    TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+    TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName};
     if (!timelineStats.stats.count(layerKey)) {
-        layerKey = {uid, kDefaultLayerName};
+        layerKey = {info.uid, kDefaultLayerName};
         timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
         timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
-        timelineStats.stats[layerKey].uid = uid;
+        timelineStats.stats[layerKey].uid = info.uid;
         timelineStats.stats[layerKey].layerName = kDefaultLayerName;
     }
 
     TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
-    updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+    updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, info.reasons);
+
+    if (info.reasons & kValidJankyReason) {
+        // TimeStats Histograms only retain positive values, so we don't need to check if these
+        // deadlines were really missed if we know that the frame had jank, since deadlines
+        // that were met will be dropped.
+        timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
+        timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
+        timeStatsLayer.deltas["appDeadlineDeltas"].insert(
+                static_cast<int32_t>(info.appDeadlineDelta));
+    }
 }
 
 void TimeStats::onDestroy(int32_t layerId) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index e76849f..fd112b9 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -121,10 +121,48 @@
     // from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
     // are no jank reasons, then total frames are incremented but jank is not, for accurate
     // accounting of janky frames.
-    // When these frame counts are incremented, these are also aggregated into a global reporting
-    // packet to help with data validation and assessing of overall device health.
-    virtual void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
-                                      const std::string& layerName, int32_t reasons) = 0;
+    // displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order
+    // to provide contextual information about a janky frame. These values may only be uploaded if
+    // there was an associated valid jank reason, and they must be positive. When these frame counts
+    // are incremented, these are also aggregated into a global reporting packet to help with data
+    // validation and assessing of overall device health.
+    struct JankyFramesInfo {
+        Fps refreshRate;
+        std::optional<Fps> renderRate;
+        uid_t uid = 0;
+        std::string layerName;
+        int32_t reasons = 0;
+        nsecs_t displayDeadlineDelta = 0;
+        nsecs_t displayPresentJitter = 0;
+        nsecs_t appDeadlineDelta = 0;
+
+        bool operator==(const JankyFramesInfo& o) const {
+            return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
+                    ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
+                     (renderRate != std::nullopt && o.renderRate != std::nullopt &&
+                      Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
+                    uid == o.uid && layerName == o.layerName && reasons == o.reasons &&
+                    displayDeadlineDelta == o.displayDeadlineDelta &&
+                    displayPresentJitter == o.displayPresentJitter &&
+                    appDeadlineDelta == o.appDeadlineDelta;
+        }
+
+        friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) {
+            os << "JankyFramesInfo {";
+            os << "\n    .refreshRate = " << info.refreshRate;
+            os << "\n    .renderRate = "
+               << (info.renderRate ? to_string(*info.renderRate) : "nullopt");
+            os << "\n    .uid = " << info.uid;
+            os << "\n    .layerName = " << info.layerName;
+            os << "\n    .reasons = " << info.reasons;
+            os << "\n    .displayDeadlineDelta = " << info.displayDeadlineDelta;
+            os << "\n    .displayPresentJitter = " << info.displayPresentJitter;
+            os << "\n    .appDeadlineDelta = " << info.appDeadlineDelta;
+            return os << "\n}";
+        }
+    };
+
+    virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0;
     // Clean up the layer record
     virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -273,8 +311,8 @@
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
                          std::optional<Fps> renderRate) override;
-    void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
-                              const std::string& layerName, int32_t reasons) override;
+
+    void incrementJankyFrames(const JankyFramesInfo& info) override;
     // Clean up the layer record
     void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index df7be1f..814f046 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -41,7 +41,7 @@
     if (delta < 0) return;
     // std::lower_bound won't work on out of range values
     if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
-        hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1];
+        hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
         return;
     }
     auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
@@ -154,6 +154,10 @@
                       ele.second.key.displayRefreshRateBucket);
         StringAppendF(&result, "renderRate = %d fps\n", ele.second.key.renderRateBucket);
         result.append(ele.second.jankPayload.toString());
+        StringAppendF(&result, "sfDeadlineMisses histogram is as below:\n");
+        result.append(ele.second.displayDeadlineDeltas.toString());
+        StringAppendF(&result, "sfPredictionErrors histogram is as below:\n");
+        result.append(ele.second.displayPresentDeltas.toString());
     }
 
     const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0144abc0..38ee888 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -121,6 +121,8 @@
     struct TimelineStats {
         TimelineStatsKey key;
         JankPayload jankPayload;
+        Histogram displayDeadlineDeltas;
+        Histogram displayPresentDeltas;
         std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
     };
 
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index a53655d..b3ab8f1 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -27,6 +27,7 @@
 #include <cinttypes>
 
 using namespace std::chrono_literals;
+using testing::_;
 using testing::AtLeast;
 using testing::Contains;
 using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
@@ -40,10 +41,6 @@
 using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType;
 using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType;
 
-MATCHER_P(HasBit, bit, "") {
-    return (arg & bit) != 0;
-}
-
 namespace android::frametimeline {
 
 class FrameTimelineTest : public testing::Test {
@@ -265,9 +262,7 @@
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
-            .Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
@@ -318,8 +313,7 @@
     // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
     int frameTimeFactor = 0;
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_))
             .Times(static_cast<int32_t>(*maxDisplayFrames));
     for (size_t i = 0; i < *maxDisplayFrames; i++) {
         auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -434,9 +428,19 @@
 
 // Tests related to TimeStats
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+    Fps refreshRate = Fps(11);
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, sUidOne, sLayerNameOne,
-                                     HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+                                                   sLayerNameOne,
+                                                   JankType::SurfaceFlingerCpuDeadlineMissed,
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count(),
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(10ms)
+                                                           .count(),
+                                                   0}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -451,7 +455,9 @@
                                                        sUidOne, sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
                                 std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
-                                Fps::fromPeriodNsecs(11));
+                                refreshRate);
+    surfaceFrame1->setAcquireFenceTime(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count());
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
@@ -462,9 +468,11 @@
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+    Fps refreshRate = Fps::fromPeriodNsecs(30);
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, sUidOne, sLayerNameOne,
-                                     HasBit(JankType::DisplayHAL)));
+                incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+                                                                sLayerNameOne, JankType::DisplayHAL,
+                                                                0, 0, 0}));
 
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
@@ -480,8 +488,10 @@
                                                        sUidOne, sLayerNameOne, sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1,
                                 std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
-                                Fps::fromPeriodNsecs(30));
+                                refreshRate);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    surfaceFrame1->setAcquireFenceTime(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count());
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
     presentFence1->signalForTest(
             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
@@ -491,9 +501,14 @@
 }
 
 TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+    Fps refreshRate = Fps(11.0);
     EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, sUidOne, sLayerNameOne,
-                                     HasBit(JankType::AppDeadlineMissed)));
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
+                                                   sLayerNameOne, JankType::AppDeadlineMissed, 0, 0,
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(25ms)
+                                                           .count()}));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
             {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -510,7 +525,7 @@
             std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1,
                                 std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
-                                Fps::fromPeriodNsecs(11));
+                                refreshRate);
 
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -522,6 +537,45 @@
     EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
 }
 
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) {
+    Fps refreshRate = Fps(11.0);
+    Fps renderRate = Fps(30.0);
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+                                                   JankType::AppDeadlineMissed, 0, 0,
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(25ms)
+                                                           .count()}));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
+                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+    surfaceFrame1->setAcquireFenceTime(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+                                refreshRate);
+
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    surfaceFrame1->setRenderRate(renderRate);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+                                 presentFence1);
+
+    EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
+}
+
 /*
  * Tracing Tests
  *
@@ -560,8 +614,7 @@
 TEST_F(FrameTimelineTest, tracing_sanityTest) {
     auto tracingSession = getTracingSessionForTest();
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -831,8 +884,7 @@
 TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
     auto tracingSession = getTracingSessionForTest();
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
 
@@ -943,8 +995,7 @@
 // Tests for Jank classification
 TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
@@ -975,10 +1026,11 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+    Fps vsyncRate = Fps::fromPeriodNsecs(11);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
-    mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
     mFrameTimeline->setSfPresent(26, presentFence1);
     auto displayFrame = getDisplayFrame(0);
     presentFence1->signalForTest(30);
@@ -988,7 +1040,7 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
     mFrameTimeline->setSfPresent(56, presentFence2);
     displayFrame = getDisplayFrame(0);
 
@@ -1002,7 +1054,6 @@
     auto displayFrame2 = getDisplayFrame(1);
     presentFence2->signalForTest(65);
     EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
-
     addEmptyDisplayFrame();
     displayFrame2 = getDisplayFrame(1);
 
@@ -1014,10 +1065,11 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+    Fps vsyncRate = Fps::fromPeriodNsecs(11);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
-    mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate);
     mFrameTimeline->setSfPresent(26, presentFence1);
     auto displayFrame = getDisplayFrame(0);
     presentFence1->signalForTest(50);
@@ -1027,7 +1079,7 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
+    mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate);
     mFrameTimeline->setSfPresent(56, presentFence2);
     displayFrame = getDisplayFrame(0);
 
@@ -1096,9 +1148,7 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
-            .Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
@@ -1152,6 +1202,14 @@
     auto actuals2 = presentedSurfaceFrame2.getActuals();
     EXPECT_EQ(actuals2.presentTime, 0);
 
+    ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
+
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
+                                                   sLayerNameOne, JankType::PredictionError, 0, 5,
+                                                   0}));
+
     addEmptyDisplayFrame();
 
     // Fences for the second frame have flushed, so the present timestamps should be updated
@@ -1168,9 +1226,7 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
-            .Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
@@ -1224,6 +1280,14 @@
     auto actuals2 = presentedSurfaceFrame2.getActuals();
     EXPECT_EQ(actuals2.presentTime, 0);
 
+    ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get());
+
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(
+                        TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne,
+                                                   sLayerNameOne, JankType::PredictionError, 0, 5,
+                                                   0}));
+
     addEmptyDisplayFrame();
 
     // Fences for the second frame have flushed, so the present timestamps should be updated
@@ -1240,8 +1304,7 @@
 }
 
 TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_));
 
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
@@ -1283,9 +1346,7 @@
     // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
     // jank to the SurfaceFrame.
 
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
-            .Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
@@ -1356,9 +1417,7 @@
 
 TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
     // Layer specific increment
-    EXPECT_CALL(*mTimeStats,
-                incrementJankyFrames(testing::_, testing::_, testing::_, testing::_, testing::_))
-            .Times(2);
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
     auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 303b4eb..35c32ec 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -366,16 +366,16 @@
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerCpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::DisplayHAL);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::AppDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::None);
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::DisplayHAL, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::AppDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::None, 1, 2, 3});
 
     const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
     std::string expectedResult =
@@ -838,16 +838,16 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
 
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerCpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::DisplayHAL);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::AppDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::None);
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::DisplayHAL, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::AppDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::None, 1, 2, 3});
 
     EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
 
@@ -987,16 +987,16 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerCpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::DisplayHAL);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::AppDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::None);
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::DisplayHAL, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::AppDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::None, 1, 2, 3});
 
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
@@ -1007,6 +1007,8 @@
     std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
     std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
     std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {});
+    std::string expectedSfDeadlineMissed = buildExpectedHistogramBytestring({1}, {4});
+    std::string expectedSfPredictionErrors = buildExpectedHistogramBytestring({2}, {4});
 
     {
         InSequence seq;
@@ -1042,14 +1044,16 @@
         EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0));
         EXPECT_CALL(*mDelegate,
                     statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(),
-                                                     expectedEmptyHistogram.size()),
-                                             expectedEmptyHistogram.size()));
+                                             BytesEq((const uint8_t*)
+                                                             expectedSfDeadlineMissed.c_str(),
+                                                     expectedSfDeadlineMissed.size()),
+                                             expectedSfDeadlineMissed.size()));
         EXPECT_CALL(*mDelegate,
                     statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(),
-                                                     expectedEmptyHistogram.size()),
-                                             expectedEmptyHistogram.size()));
+                                             BytesEq((const uint8_t*)
+                                                             expectedSfPredictionErrors.c_str(),
+                                                     expectedSfPredictionErrors.size()),
+                                             expectedSfPredictionErrors.size()));
         EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0));
 
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
@@ -1083,16 +1087,16 @@
     }
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerCpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::SurfaceFlingerGpuDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::DisplayHAL);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::AppDeadlineMissed);
-    mTimeStats->incrementJankyFrames(kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
-                                     JankType::None);
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::DisplayHAL, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::AppDeadlineMissed, 1, 2, 3});
+    mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+                                      JankType::None, 1, 2, 3});
 
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
@@ -1107,7 +1111,7 @@
     std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
     std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
     std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
-    std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {});
+    std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3}, {4});
     {
         InSequence seq;
         EXPECT_CALL(*mDelegate,
@@ -1174,9 +1178,10 @@
                                              expectedFrameRateOverride.size()));
         EXPECT_CALL(*mDelegate,
                     statsEventWriteByteArray(mDelegate->mEvent,
-                                             BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(),
-                                                     expectedEmptyHistogram.size()),
-                                             expectedEmptyHistogram.size()));
+                                             BytesEq((const uint8_t*)
+                                                             expectedAppDeadlineMissed.c_str(),
+                                                     expectedAppDeadlineMissed.size()),
+                                             expectedAppDeadlineMissed.size()));
 
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ebea5a0..3e4a0b8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -52,8 +52,7 @@
     MOCK_METHOD5(setPresentFence,
                  void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
                       std::optional<Fps>));
-    MOCK_METHOD5(incrementJankyFrames,
-                 void(Fps, std::optional<Fps>, uid_t, const std::string&, int32_t));
+    MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
     MOCK_METHOD1(setPowerMode,