SF: pass DisplayMode to VsyncTracker

This will be used later to get the peak refresh rate from the predictor
for sub-frame jank recover in VRR case.

Bug: 296635687
Test: presubmit
Change-Id: I1e108223b6ae4872bb48a38e4af743da565749cd
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 6d2586a..db247aa 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -21,6 +21,7 @@
         "libui",
         "libutils",
     ],
+    static_libs: ["libsurfaceflinger_common"],
 }
 
 cc_library_headers {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b54f334..29b1d62 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -118,7 +118,7 @@
 
 void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
     auto schedulePtr = std::make_shared<VsyncSchedule>(
-            displayId, mFeatures,
+            selectorPtr->getActiveMode().modePtr, mFeatures,
             [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); },
             mVsyncTrackerCallback);
 
@@ -503,7 +503,7 @@
 }
 
 void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable,
-                                            std::optional<Fps> refreshRate) {
+                                            DisplayModePtr modePtr) {
     const auto displayOpt = mDisplays.get(id);
     if (!displayOpt) {
         ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str());
@@ -512,12 +512,12 @@
     const Display& display = *displayOpt;
 
     if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) {
-        if (!refreshRate) {
-            refreshRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate();
+        if (!modePtr) {
+            modePtr = display.selectorPtr->getActiveMode().modePtr.get();
         }
-        if (refreshRate->isValid()) {
+        if (modePtr->getVsyncRate().isValid()) {
             constexpr bool kForce = false;
-            display.schedulePtr->startPeriodTransition(refreshRate->getPeriod(), kForce);
+            display.schedulePtr->onDisplayModeChanged(ftl::as_non_null(modePtr), kForce);
         }
     }
 }
@@ -563,19 +563,7 @@
     ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
           to_string(mode.modePtr->getVsyncRate()).c_str());
 
-    display.schedulePtr->getTracker().setDisplayModeData(
-            {.renderRate = renderFrameRate,
-             .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)});
-}
-
-std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) {
-    if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) {
-        return Period::fromNs(
-                mode.modePtr->getVrrConfig()
-                        ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs);
-    } else {
-        return std::nullopt;
-    }
+    display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
 }
 
 void Scheduler::resync() {
@@ -913,9 +901,9 @@
 
         newVsyncSchedulePtr = pacesetter.schedulePtr;
 
-        const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate();
         constexpr bool kForce = true;
-        newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce);
+        newVsyncSchedulePtr->onDisplayModeChanged(pacesetter.selectorPtr->getActiveMode().modePtr,
+                                                  kForce);
     }
     return newVsyncSchedulePtr;
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c78051a..0615b31 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -210,13 +210,12 @@
     // If allowToEnable is true, then hardware vsync will be turned on.
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
-    // If refreshRate is nullopt, use the existing refresh rate of the display.
+    // If modePtr is nullopt, use the active display mode.
     void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable,
-                               std::optional<Fps> refreshRate = std::nullopt)
-            EXCLUDES(mDisplayLock) {
+                               DisplayModePtr modePtr = nullptr) EXCLUDES(mDisplayLock) {
         std::scoped_lock lock(mDisplayLock);
         ftl::FakeGuard guard(kMainThreadContext);
-        resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate);
+        resyncToHardwareVsyncLocked(id, allowToEnable, modePtr);
     }
     void forceNextResync() { mLastResyncTime = 0; }
 
@@ -354,7 +353,7 @@
     void onHardwareVsyncRequest(PhysicalDisplayId, bool enable);
 
     void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable,
-                                     std::optional<Fps> refreshRate = std::nullopt)
+                                     DisplayModePtr modePtr = nullptr)
             REQUIRES(kMainThreadContext, mDisplayLock);
     void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
     void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
@@ -431,9 +430,6 @@
     Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
     void resync() override EXCLUDES(mDisplayLock);
 
-    std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&)
-            REQUIRES(mDisplayLock);
-
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
         sp<EventThreadConnection> connection;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index f5f93ce..acb7265 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -47,16 +47,16 @@
 
 VSyncPredictor::~VSyncPredictor() = default;
 
-VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize,
+VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
                                size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
                                IVsyncTrackerCallback& callback)
-      : mId(id),
+      : mId(modePtr->getPhysicalDisplayId()),
         mTraceOn(property_get_bool("debug.sf.vsp_trace", false)),
         kHistorySize(historySize),
         kMinimumSamplesForPrediction(minimumSamplesForPrediction),
         kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
         mVsyncTrackerCallback(callback),
-        mIdealPeriod(idealPeriod) {
+        mDisplayModePtr(modePtr) {
     resetModel();
 }
 
@@ -74,13 +74,18 @@
     return (i + 1) % mTimestamps.size();
 }
 
+nsecs_t VSyncPredictor::idealPeriod() const {
+    return mDisplayModePtr->getVsyncRate().getPeriodNsecs();
+}
+
 bool VSyncPredictor::validate(nsecs_t timestamp) const {
     if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
         return true;
     }
 
-    auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
-    auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
+    const auto aValidTimestamp = mTimestamps[mLastTimestampIndex];
+    const auto percent =
+            (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
     if (percent >= kOutlierTolerancePercent &&
         percent <= (kMaxPercent - kOutlierTolerancePercent)) {
         return false;
@@ -90,7 +95,7 @@
                                        [timestamp](nsecs_t a, nsecs_t b) {
                                            return std::abs(timestamp - a) < std::abs(timestamp - b);
                                        });
-    const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod;
+    const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod();
     if (distancePercent < kOutlierTolerancePercent) {
         // duplicate timestamp
         return false;
@@ -100,7 +105,7 @@
 
 nsecs_t VSyncPredictor::currentPeriod() const {
     std::lock_guard lock(mMutex);
-    return mRateMap.find(mIdealPeriod)->second.slope;
+    return mRateMap.find(idealPeriod())->second.slope;
 }
 
 bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -137,7 +142,7 @@
 
     const size_t numSamples = mTimestamps.size();
     if (numSamples < kMinimumSamplesForPrediction) {
-        mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+        mRateMap[idealPeriod()] = {idealPeriod(), 0};
         return true;
     }
 
@@ -161,7 +166,7 @@
 
     // Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
     const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
-    auto it = mRateMap.find(mIdealPeriod);
+    auto it = mRateMap.find(idealPeriod());
     auto const currentPeriod = it->second.slope;
 
     // The mean of the ordinals must be precise for the intercept calculation, so scale them up for
@@ -199,7 +204,7 @@
     }
 
     if (CC_UNLIKELY(bottom == 0)) {
-        it->second = {mIdealPeriod, 0};
+        it->second = {idealPeriod(), 0};
         clearTimestamps();
         return false;
     }
@@ -207,9 +212,9 @@
     nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
     nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
 
-    auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
+    auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
     if (percent >= kOutlierTolerancePercent) {
-        it->second = {mIdealPeriod, 0};
+        it->second = {idealPeriod(), 0};
         clearTimestamps();
         return false;
     }
@@ -241,8 +246,8 @@
     if (mTimestamps.empty()) {
         traceInt64("VSP-mode", 1);
         auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
-        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
-        return knownTimestamp + numPeriodsOut * mIdealPeriod;
+        auto const numPeriodsOut = ((timePoint - knownTimestamp) / idealPeriod()) + 1;
+        return knownTimestamp + numPeriodsOut * idealPeriod();
     }
 
     auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
@@ -278,11 +283,11 @@
     mLastVsyncSequence = getVsyncSequenceLocked(timePoint);
 
     const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int {
-        if (!mDisplayModeDataOpt) return 0;
+        if (!mRenderRateOpt) return 0;
 
         const auto divisor =
-                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod),
-                                                         mDisplayModeDataOpt->renderRate);
+                RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+                                                         *mRenderRateOpt);
         if (divisor <= 1) return 0;
 
         const int mod = mLastVsyncSequence->seq % divisor;
@@ -293,12 +298,12 @@
 
     if (renderRatePhase == 0) {
         const auto vsyncTime = mLastVsyncSequence->vsyncTime;
-        if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+        if (FlagManager::getInstance().vrr_config()) {
             const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
             ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__,
                           ticks<std::milli, float>(vsyncTimePoint - TimePoint::now()));
-            mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt,
-                                                   Period::fromNs(mIdealPeriod));
+            const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
+            mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
         }
         return vsyncTime;
     }
@@ -307,12 +312,13 @@
     const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase;
     const auto nextAnticipatedVsyncTime =
             nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2);
-    if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) {
+    if (FlagManager::getInstance().vrr_config()) {
         const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime);
         ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__,
                       ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now()));
-        mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint,
-                                               *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod));
+        const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
+        mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr,
+                                               renderRate);
     }
     return nextAnticipatedVsyncTime;
 }
@@ -328,7 +334,8 @@
 bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const {
     std::lock_guard lock(mMutex);
     const auto divisor =
-            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate);
+            RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()),
+                                                     frameRate);
     return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor));
 }
 
@@ -344,7 +351,7 @@
         return true;
     }
 
-    const nsecs_t period = mRateMap[mIdealPeriod].slope;
+    const nsecs_t period = mRateMap[idealPeriod()].slope;
     const nsecs_t justBeforeTimePoint = timePoint - period / 2;
     const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint);
     ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64,
@@ -352,14 +359,38 @@
     return vsyncSequence.seq % divisor == 0;
 }
 
-void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) {
-    ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
-          to_string(displayModeData.renderRate).c_str(),
-          displayModeData.notifyExpectedPresentTimeoutOpt
-                  ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str()
-                  : "N/A");
+void VSyncPredictor::setRenderRate(Fps renderRate) {
+    ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
+    ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
     std::lock_guard lock(mMutex);
-    mDisplayModeDataOpt = displayModeData;
+    mRenderRateOpt = renderRate;
+}
+
+void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) {
+    LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(),
+                        "mode does not belong to the display");
+    ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str());
+    const auto timeout = modePtr->getVrrConfig()
+            ? modePtr->getVrrConfig()->notifyExpectedPresentConfig
+            : std::nullopt;
+    ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(),
+          to_string(*modePtr).c_str(),
+          timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A");
+    std::lock_guard lock(mMutex);
+
+    mDisplayModePtr = modePtr;
+    traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
+
+    static constexpr size_t kSizeLimit = 30;
+    if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+        mRateMap.erase(mRateMap.begin());
+    }
+
+    if (mRateMap.find(idealPeriod()) == mRateMap.end()) {
+        mRateMap[idealPeriod()] = {idealPeriod(), 0};
+    }
+
+    clearTimestamps();
 }
 
 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
@@ -369,26 +400,7 @@
 }
 
 VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
-    return mRateMap.find(mIdealPeriod)->second;
-}
-
-void VSyncPredictor::setPeriod(nsecs_t period) {
-    ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str());
-    traceInt64("VSP-setPeriod", period);
-
-    std::lock_guard lock(mMutex);
-    static constexpr size_t kSizeLimit = 30;
-    if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
-        mRateMap.erase(mRateMap.begin());
-    }
-
-    // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData
-    mIdealPeriod = period;
-    if (mRateMap.find(period) == mRateMap.end()) {
-        mRateMap[mIdealPeriod] = {period, 0};
-    }
-
-    clearTimestamps();
+    return mRateMap.find(idealPeriod())->second;
 }
 
 void VSyncPredictor::clearTimestamps() {
@@ -412,18 +424,18 @@
 
 void VSyncPredictor::resetModel() {
     std::lock_guard lock(mMutex);
-    mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+    mRateMap[idealPeriod()] = {idealPeriod(), 0};
     clearTimestamps();
 }
 
 void VSyncPredictor::dump(std::string& result) const {
     std::lock_guard lock(mMutex);
-    StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+    StringAppendF(&result, "\tmDisplayModePtr=%s\n", to_string(*mDisplayModePtr).c_str());
     StringAppendF(&result, "\tRefresh Rate Map:\n");
-    for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+    for (const auto& [period, periodInterceptTuple] : mRateMap) {
         StringAppendF(&result,
                       "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
-                      idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+                      period / 1e6f, periodInterceptTuple.slope / 1e6f,
                       periodInterceptTuple.intercept);
     }
 }
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c271eb7..fbc1e16 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -31,14 +31,14 @@
 public:
     /*
      * \param [in] PhysicalDisplayid The display this corresponds to.
-     * \param [in] idealPeriod  The initial ideal period to use.
+     * \param [in] modePtr  The initial display mode
      * \param [in] historySize  The internal amount of entries to store in the model.
      * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
      * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
      * samples that fall outlierTolerancePercent from an anticipated vsync event.
      * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker.
      */
-    VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize,
+    VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize,
                    size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent,
                    IVsyncTrackerCallback&);
     ~VSyncPredictor();
@@ -48,15 +48,6 @@
     nsecs_t currentPeriod() const final EXCLUDES(mMutex);
     void resetModel() final EXCLUDES(mMutex);
 
-    /*
-     * Inform the model that the period is anticipated to change to a new value.
-     * model will use the period parameter to predict vsync events until enough
-     * timestamps with the new period have been collected.
-     *
-     * \param [in] period   The new period that should be used.
-     */
-    void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
-
     /* Query if the model is in need of more samples to make a prediction.
      * \return  True, if model would benefit from more samples, False if not.
      */
@@ -71,7 +62,9 @@
 
     bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex);
 
-    void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex);
+    void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
+
+    void setRenderRate(Fps) final EXCLUDES(mMutex);
 
     void dump(std::string& result) const final EXCLUDES(mMutex);
 
@@ -96,6 +89,7 @@
         int64_t seq;
     };
     VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex);
+    nsecs_t idealPeriod() const REQUIRES(mMutex);
 
     bool const mTraceOn;
     size_t const kHistorySize;
@@ -104,7 +98,6 @@
     IVsyncTrackerCallback& mVsyncTrackerCallback;
     std::mutex mutable mMutex;
 
-    nsecs_t mIdealPeriod GUARDED_BY(mMutex);
     std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
 
     // Map between ideal vsync period and the calculated model
@@ -113,7 +106,8 @@
     size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
     std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
 
-    std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex);
+    ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
+    std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex);
 
     mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex);
 };
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 2938aa3..24737e4 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -116,32 +116,34 @@
     }
 }
 
-void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) {
     ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
     mPeriodConfirmationInProgress = true;
-    mPeriodTransitioningTo = newPeriod;
+    mModePtrTransitioningTo = modePtr.get();
     mMoreSamplesNeeded = true;
     setIgnorePresentFencesInternal(true);
 }
 
 void VSyncReactor::endPeriodTransition() {
     ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value);
-    mPeriodTransitioningTo.reset();
+    mModePtrTransitioningTo.reset();
     mPeriodConfirmationInProgress = false;
     mLastHwVsync.reset();
 }
 
-void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) {
-    ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period);
+void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) {
+    ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(),
+                 modePtr->getVsyncRate().getPeriodNsecs());
     std::lock_guard lock(mMutex);
     mLastHwVsync.reset();
 
-    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) {
+    if (!mSupportKernelIdleTimer &&
+        modePtr->getVsyncRate().getPeriodNsecs() == mTracker.currentPeriod() && !force) {
         endPeriodTransition();
         setIgnorePresentFencesInternal(false);
         mMoreSamplesNeeded = false;
     } else {
-        startPeriodTransitionInternal(period);
+        startPeriodTransitionInternal(modePtr);
     }
 }
 
@@ -159,14 +161,16 @@
         return false;
     }
 
-    const bool periodIsChanging =
-            mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
+    const std::optional<Period> newPeriod = mModePtrTransitioningTo
+            ? mModePtrTransitioningTo->getVsyncRate().getPeriod()
+            : std::optional<Period>{};
+    const bool periodIsChanging = newPeriod && (newPeriod->ns() != mTracker.currentPeriod());
     if (mSupportKernelIdleTimer && !periodIsChanging) {
         // Clear out the Composer-provided period and use the allowance logic below
         HwcVsyncPeriod = {};
     }
 
-    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
+    auto const period = newPeriod ? newPeriod->ns() : mTracker.currentPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
     auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -185,8 +189,8 @@
     std::lock_guard lock(mMutex);
     if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
         ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value);
-        if (mPeriodTransitioningTo) {
-            mTracker.setPeriod(*mPeriodTransitioningTo);
+        if (mModePtrTransitioningTo) {
+            mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo));
             *periodFlushed = true;
         }
 
@@ -228,10 +232,11 @@
                   mInternalIgnoreFences, mExternalIgnoreFences);
     StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
                   mMoreSamplesNeeded, mPeriodConfirmationInProgress);
-    if (mPeriodTransitioningTo) {
-        StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+    if (mModePtrTransitioningTo) {
+        StringAppendF(&result, "mModePtrTransitioningTo=%s\n",
+                      to_string(*mModePtrTransitioningTo).c_str());
     } else {
-        StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+        StringAppendF(&result, "mModePtrTransitioningTo=nullptr\n");
     }
 
     if (mLastHwVsync) {
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index f230242..2415a66 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -27,6 +27,7 @@
 
 #include <scheduler/TimeKeeper.h>
 
+#include "VSyncTracker.h"
 #include "VsyncController.h"
 
 namespace android::scheduler {
@@ -45,7 +46,7 @@
     bool addPresentFence(std::shared_ptr<FenceTime>) final;
     void setIgnorePresentFences(bool ignore) final;
 
-    void startPeriodTransition(nsecs_t period, bool force) final;
+    void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) final;
 
     bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                              bool* periodFlushed) final;
@@ -57,7 +58,7 @@
 private:
     void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
     void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
-    void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
+    void startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr>) REQUIRES(mMutex);
     void endPeriodTransition() REQUIRES(mMutex);
     bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
             REQUIRES(mMutex);
@@ -74,7 +75,7 @@
 
     bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
     bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
-    std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+    DisplayModePtr mModePtrTransitioningTo GUARDED_BY(mMutex);
     std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
 
     hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 7eedc31..2ae5c0b 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -20,25 +20,16 @@
 #include <utils/Timers.h>
 
 #include <scheduler/Fps.h>
+#include <scheduler/FrameRateMode.h>
 
 #include "VSyncDispatch.h"
 
 namespace android::scheduler {
 
-struct DisplayModeData {
-    Fps renderRate;
-    std::optional<Period> notifyExpectedPresentTimeoutOpt;
-
-    bool operator==(const DisplayModeData& other) const {
-        return isApproxEqual(renderRate, other.renderRate) &&
-                notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt;
-    }
-};
-
 struct IVsyncTrackerCallback {
     virtual ~IVsyncTrackerCallback() = default;
-    virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime,
-                                  const DisplayModeData&, Period vsyncPeriod) = 0;
+    virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
+                                  Fps renderRate) = 0;
 };
 
 /*
@@ -77,13 +68,6 @@
      */
     virtual nsecs_t currentPeriod() const = 0;
 
-    /*
-     * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
-     *
-     * \param [in] period   The period that the system is changing into.
-     */
-    virtual void setPeriod(nsecs_t period) = 0;
-
     /* Inform the tracker that the samples it has are not accurate for prediction. */
     virtual void resetModel() = 0;
 
@@ -98,20 +82,25 @@
     virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0;
 
     /*
-     * Sets the metadata about the currently active display mode such as VRR
-     * timeout period, vsyncPeriod and framework property such as render rate.
-     * If the render rate is not a divisor of the period, the render rate is
-     * ignored until the period changes.
+     * Sets the active mode of the display which includes the vsync period and other VRR attributes.
+     * This will inform the tracker that the period is changing and the tracker needs to recalibrate
+     * itself.
+     *
+     * \param [in] DisplayModePtr The display mode the tracker will use.
+     */
+    virtual void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) = 0;
+
+    /*
+     * Sets a render rate on the tracker. If the render rate is not a divisor
+     * of the period, the render rate is ignored until the period changes.
      * The tracker will continue to track the vsync timeline and expect it
      * to match the current period, however, nextAnticipatedVSyncTimeFrom will
      * return vsyncs according to the render rate set. Setting a render rate is useful
      * when a display is running at 120Hz but the render frame rate is 60Hz.
-     * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along
-     * the vsyncPeriod, render rate and timeoutNs.
      *
-     * \param [in] DisplayModeData The DisplayModeData the tracker will use.
+     * \param [in] Fps   The render rate the tracker should operate at.
      */
-    virtual void setDisplayModeData(const DisplayModeData&) = 0;
+    virtual void setRenderRate(Fps) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 9177899..807a7fb 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -22,6 +22,7 @@
 
 #include <DisplayHardware/HWComposer.h>
 #include <DisplayHardware/Hal.h>
+#include <scheduler/FrameRateMode.h>
 #include <ui/FenceTime.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
@@ -59,13 +60,14 @@
                                      bool* periodFlushed) = 0;
 
     /*
-     * Inform the controller that the period is changing and the controller needs to recalibrate
-     * itself. The controller will end the period transition internally.
+     * Inform the controller that the display mode is changing and the controller needs to
+     * recalibrate itself to the new vsync period. The controller will end the period transition
+     * internally.
      *
-     * \param [in] period   The period that the system is changing into.
+     * \param [in] DisplayModePtr  The new mode the display is changing to.
      * \param [in] force    True to recalibrate even if period matches the existing period.
      */
-    virtual void startPeriodTransition(nsecs_t period, bool force) = 0;
+    virtual void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) = 0;
 
     /*
      * Tells the tracker to stop using present fences to get a vsync signal.
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
index 5fb53f9..4a8aac6 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -16,6 +16,8 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include <common/FlagManager.h>
+
 #include <ftl/fake_guard.h>
 #include <scheduler/Fps.h>
 #include <scheduler/Timer.h>
@@ -53,14 +55,14 @@
     VSyncCallbackRegistration mRegistration;
 };
 
-VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features,
+VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features,
                              RequestHardwareVsync requestHardwareVsync,
                              IVsyncTrackerCallback& callback)
-      : mId(id),
+      : mId(modePtr->getPhysicalDisplayId()),
         mRequestHardwareVsync(std::move(requestHardwareVsync)),
-        mTracker(createTracker(id, callback)),
+        mTracker(createTracker(modePtr, callback)),
         mDispatch(createDispatch(mTracker)),
-        mController(createController(id, *mTracker, features)),
+        mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)),
         mTracer(features.test(Feature::kTracePredictedVsync)
                         ? std::make_unique<PredictedVsyncTracer>(mDispatch)
                         : nullptr) {}
@@ -101,17 +103,15 @@
     mDispatch->dump(out);
 }
 
-VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id,
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr,
                                                        IVsyncTrackerCallback& callback) {
     // TODO(b/144707443): Tune constants.
-    constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
     constexpr size_t kHistorySize = 20;
     constexpr size_t kMinSamplesForPrediction = 6;
     constexpr uint32_t kDiscardOutlierPercent = 20;
 
-    return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize,
-                                            kMinSamplesForPrediction, kDiscardOutlierPercent,
-                                            callback);
+    return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction,
+                                            kDiscardOutlierPercent, callback);
 }
 
 VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) {
@@ -140,9 +140,9 @@
     return reactor;
 }
 
-void VsyncSchedule::startPeriodTransition(Period period, bool force) {
+void VsyncSchedule::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) {
     std::lock_guard<std::mutex> lock(mHwVsyncLock);
-    mController->startPeriodTransition(period.ns(), force);
+    mController->onDisplayModeChanged(modePtr, force);
     enableHardwareVsyncLocked();
 }
 
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index ca61f87..60c4105 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -57,21 +57,22 @@
 public:
     using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>;
 
-    VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&);
+    VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync,
+                  IVsyncTrackerCallback&);
     ~VsyncSchedule();
 
     // IVsyncSource overrides:
     Period period() const override;
     TimePoint vsyncDeadlineAfter(TimePoint) const override;
 
-    // Inform the schedule that the period is changing and the schedule needs to recalibrate
-    // itself. The schedule will end the period transition internally. This will
-    // enable hardware VSYNCs in order to calibrate.
+    // Inform the schedule that the display mode changed the schedule needs to recalibrate
+    // itself to the new vsync period. The schedule will end the period transition internally.
+    // This will enable hardware VSYNCs in order to calibrate.
     //
-    // \param [in] period   The period that the system is changing into.
+    // \param [in] DisplayModePtr  The mode that the display is changing to.
     // \param [in] force    True to force a transition even if it is not a
     //                      change.
-    void startPeriodTransition(Period period, bool force);
+    void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force);
 
     // Pass a VSYNC sample to VsyncController. Return true if
     // VsyncController detected that the VSYNC period changed. Enable or disable
@@ -125,7 +126,7 @@
     friend class android::VsyncScheduleTest;
     friend class android::fuzz::SchedulerFuzzer;
 
-    static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&);
+    static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&);
     static DispatchPtr createDispatch(TrackerPtr);
     static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags);