SF: Add setFrameRate vote to TimeStats

Test: SF unit tests
Bug: 172939060
Change-Id: I60ea90c6f31b4bb22f39dea654de14e6c354974c
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 4ace4c2..b6764d0 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -325,6 +325,37 @@
     mRefreshPending = false;
     return hasReadyFrame();
 }
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+    using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+    using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+    const auto frameRateCompatibility = [frameRate] {
+        switch (frameRate.type) {
+            case Layer::FrameRateCompatibility::Default:
+                return FrameRateCompatibility::Default;
+            case Layer::FrameRateCompatibility::ExactOrMultiple:
+                return FrameRateCompatibility::ExactOrMultiple;
+            default:
+                return FrameRateCompatibility::Undefined;
+        }
+    }();
+
+    const auto seamlessness = [frameRate] {
+        switch (frameRate.seamlessness) {
+            case scheduler::Seamlessness::OnlySeamless:
+                return Seamlessness::ShouldBeSeamless;
+            case scheduler::Seamlessness::SeamedAndSeamless:
+                return Seamlessness::NotRequired;
+            default:
+                return Seamlessness::Undefined;
+        }
+    }();
+
+    return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+                                       .frameRateCompatibility = frameRateCompatibility,
+                                       .seamlessness = seamlessness};
+}
+} // namespace
 
 bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
@@ -377,7 +408,9 @@
     const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
     if (presentFence->isValid()) {
         mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
-                                              refreshRate, renderRate);
+                                              refreshRate, renderRate,
+                                              frameRateToSetFrameRateVotePayload(
+                                                      mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -389,7 +422,9 @@
         // timestamp instead.
         const nsecs_t actualPresentTime = display->getRefreshTimestamp();
         mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
-                                             refreshRate, renderRate);
+                                             refreshRate, renderRate,
+                                             frameRateToSetFrameRateVotePayload(
+                                                     mDrawingState.frameRate));
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                actualPresentTime,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 974ae84..2094972 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -89,12 +89,15 @@
     return byteString;
 }
 
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+        float refreshRate,
+        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+        TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
     util::ProtoOutputStream proto;
     proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+                static_cast<int>(frameRateCompatibility));
+    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
 
     std::string byteString;
     proto.serializeToString(&byteString);
@@ -229,7 +232,10 @@
         mStatsDelegate->statsEventWriteInt32(
                 event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
         mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
-        std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
+        std::string frameRateVoteBytes =
+                frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate,
+                                               layer->setFrameRateVote.frameRateCompatibility,
+                                               layer->setFrameRateVote.seamlessness);
         mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
                                                  frameRateVoteBytes.size()); // set_frame_rate_vote
         std::string appDeadlineMissedBytes =
@@ -468,8 +474,10 @@
 }
 
 void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                                   std::optional<Fps> renderRate) {
+                                                   std::optional<Fps> renderRate,
+                                                   SetFrameRateVote frameRateVote) {
     ATRACE_CALL();
+    ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
 
     LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
@@ -501,6 +509,9 @@
                 displayStats.stats[layerKey].uid = uid;
                 displayStats.stats[layerKey].layerName = layerName;
             }
+            if (frameRateVote.frameRate > 0.0f) {
+                displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+            }
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
@@ -724,7 +735,8 @@
 }
 
 void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                               Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                               Fps displayRefreshRate, std::optional<Fps> renderRate,
+                               SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -743,12 +755,13 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) {
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -768,7 +781,7 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+    flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
 }
 
 static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index fd112b9..a87b7cb 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -50,6 +50,8 @@
 
 class TimeStats {
 public:
+    using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
     virtual ~TimeStats() = default;
 
     // Called once boot has been finished to perform additional capabilities,
@@ -110,10 +112,12 @@
     // SetPresent{Time, Fence} are not expected to be called in the critical
     // rendering path, as they flush prior fences if those fences have fired.
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                                Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                SetFrameRateVote frameRateVote) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence,
-                                 Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+                                 Fps displayRefreshRate, std::optional<Fps> renderRate,
+                                 SetFrameRateVote frameRateVote) = 0;
 
     // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
     // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -307,10 +311,11 @@
     void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
     void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
-                        Fps displayRefreshRate, std::optional<Fps> renderRate) override;
+                        Fps displayRefreshRate, std::optional<Fps> renderRate,
+                        SetFrameRateVote frameRateVote) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
-                         std::optional<Fps> renderRate) override;
+                         std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
 
     void incrementJankyFrames(const JankyFramesInfo& info) override;
     // Clean up the layer record
@@ -334,7 +339,8 @@
     AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
     bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
     void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
-                                            std::optional<Fps> renderRate);
+                                            std::optional<Fps> renderRate,
+                                            SetFrameRateVote frameRateVote);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
     bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d116b02..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -91,6 +91,37 @@
     return result;
 }
 
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+    switch (compatibility) {
+        case FrameRateCompatibility::Undefined:
+            return "Undefined";
+        case FrameRateCompatibility::Default:
+            return "Default";
+        case FrameRateCompatibility::ExactOrMultiple:
+            return "ExactOrMultiple";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+    switch (seamlessness) {
+        case Seamlessness::Undefined:
+            return "Undefined";
+        case Seamlessness::ShouldBeSeamless:
+            return "ShouldBeSeamless";
+        case Seamlessness::NotRequired:
+            return "NotRequired";
+    }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+    std::string result;
+    StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+    StringAppendF(&result, "frameRateCompatibility = %s\n",
+                  toString(frameRateCompatibility).c_str());
+    StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+    return result;
+}
+
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
     StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -104,6 +135,8 @@
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     result.append("Jank payload for this layer:\n");
     result.append(jankPayload.toString());
+    result.append("SetFrateRate vote for this layer:\n");
+    result.append(setFrameRateVote.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         const float averageTime = iter->second.averageTime();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5ee28ce..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -55,6 +55,28 @@
         std::string toString() const;
     };
 
+    struct SetFrameRateVote {
+        float frameRate = 0;
+
+        // Needs to be in sync with atoms.proto
+        enum class FrameRateCompatibility {
+            Undefined = 0,
+            Default = 1,
+            ExactOrMultiple = 2,
+        } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+        // Needs to be in sync with atoms.proto
+        enum class Seamlessness {
+            Undefined = 0,
+            ShouldBeSeamless = 1,
+            NotRequired = 2,
+        } seamlessness = Seamlessness::Undefined;
+
+        static std::string toString(FrameRateCompatibility);
+        static std::string toString(Seamlessness);
+        std::string toString() const;
+    };
+
     class TimeStatsLayer {
     public:
         uid_t uid;
@@ -67,6 +89,7 @@
         int32_t lateAcquireFrames = 0;
         int32_t badDesiredPresentFrames = 0;
         JankPayload jankPayload;
+        SetFrameRateVote setFrameRateVote;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a72ab42..c1f0c4e 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -142,15 +142,16 @@
 
     std::string inputCommand(InputCommand cmd, bool useProto);
 
-    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+    void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                      TimeStats::SetFrameRateVote frameRateVote);
 
     int32_t genRandomInt32(int32_t begin, int32_t end);
 
     template <size_t N>
     void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
-                          nsecs_t ts) {
+                          nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
         for (size_t i = 0; i < N; i++, ts += 1000000) {
-            setTimeStamp(sequence[i], id, frameNumber, ts);
+            setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
         }
     }
 
@@ -234,7 +235,8 @@
     return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
 }
 
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+                                 TimeStats::SetFrameRateVote frameRateVote) {
     switch (type) {
         case TimeStamp::POST:
             ASSERT_NO_FATAL_FAILURE(
@@ -254,13 +256,13 @@
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
             break;
         case TimeStamp::PRESENT:
-            ASSERT_NO_FATAL_FAILURE(
-                    mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+                                                               kRenderRate0, frameRateVote));
             break;
         case TimeStamp::PRESENT_FENCE:
-            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
-                                                                std::make_shared<FenceTime>(ts),
-                                                                kRefreshRate0, kRenderRate0));
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+                                                kRefreshRate0, kRenderRate0, frameRateVote));
             break;
         default:
             ALOGD("Invalid timestamp type");
@@ -411,6 +413,96 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 60.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = Default";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = ShouldBeSeamless";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+    const auto frameRate90 = TimeStats::SetFrameRateVote{
+            .frameRate = 90.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    const auto frameRateDefault = TimeStats::SetFrameRateVote{
+            .frameRate = 0.0f,
+            .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+    std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+    result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+    expectedResult = "frameRate = 90.00";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "frameRateCompatibility = ExactOrMultiple";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "seamlessness = NotRequired";
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
 TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
     // this stat is not in the proto so verify by checking the string dump
     constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -936,12 +1028,15 @@
     return byteString;
 }
 
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
-                                           int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+        float refreshRate,
+        TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+        TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
     util::ProtoOutputStream proto;
     proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
-    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
-    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+    proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+                static_cast<int>(frameRateCompatibility));
+    proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
 
     std::string byteString;
     proto.serializeToString(&byteString);
@@ -1149,7 +1244,13 @@
     for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
         mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
     }
-    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    const auto frameRate60 = TimeStats::SetFrameRateVote{
+            .frameRate = 60.0f,
+            .frameRateCompatibility =
+                    TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+            .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+    };
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
 
     mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
                                       JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
@@ -1181,7 +1282,10 @@
     std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
     std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
     std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
-    std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
+    std::string expectedFrameRateOverride =
+            frameRateVoteToProtoByteString(frameRate60.frameRate,
+                                           frameRate60.frameRateCompatibility,
+                                           frameRate60.seamlessness);
     std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
     {
         InSequence seq;
@@ -1455,7 +1559,7 @@
         TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
         const int32_t ts = genRandomInt32(1, 1000000000);
         ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
-        setTimeStamp(type, layerId, frameNumber, ts);
+        setTimeStamp(type, layerId, frameNumber, ts, {});
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 3e4a0b8..37b74ed 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -48,10 +48,11 @@
     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_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>));
-    MOCK_METHOD5(setPresentFence,
-                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
-                      std::optional<Fps>));
+    MOCK_METHOD6(setPresentTime,
+                 void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+    MOCK_METHOD6(setPresentFence,
+                 void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+                      SetFrameRateVote));
     MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));