[SF] Send NotifyExpectedPresentHint at the transaction time

BUG: 314199179
BUG: 315989692
BUG: 284845445

Test: atest NotifyExpectedPresentTest
Change-Id: Ief7fff69e8b92f0affd228b2bdcdc5009806f905
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index a145e59..ff88d71 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -17,7 +17,6 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <binder/IPCThreadState.h>
-#include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/threads.h>
@@ -66,7 +65,7 @@
     {
         std::lock_guard lock(mVsync.mutex);
         mVsync.lastCallbackTime = expectedVsyncTime;
-        mVsync.scheduledFrameTime.reset();
+        mVsync.scheduledFrameTimeOpt.reset();
     }
 
     const auto vsyncId = VsyncId{mVsync.tokenManager->generateTokenForPredictions(
@@ -122,7 +121,7 @@
                                                             std::placeholders::_3),
                                                   "sf");
     if (reschedule) {
-        mVsync.scheduledFrameTime =
+        mVsync.scheduledFrameTimeOpt =
                 mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                                .readyDuration = 0,
                                                .lastVsync = mVsync.lastCallbackTime.ns()});
@@ -140,7 +139,7 @@
     ATRACE_CALL();
     std::lock_guard lock(mVsync.mutex);
     mVsync.workDuration = workDuration;
-    mVsync.scheduledFrameTime =
+    mVsync.scheduledFrameTimeOpt =
             mVsync.registration->update({.workDuration = mVsync.workDuration.get().count(),
                                          .readyDuration = 0,
                                          .lastVsync = mVsync.lastCallbackTime.ns()});
@@ -193,22 +192,20 @@
     ATRACE_CALL();
 
     std::lock_guard lock(mVsync.mutex);
-    mVsync.scheduledFrameTime =
+    mVsync.scheduledFrameTimeOpt =
             mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                            .readyDuration = 0,
                                            .lastVsync = mVsync.lastCallbackTime.ns()});
 }
 
-auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
+std::optional<scheduler::ScheduleResult> MessageQueue::getScheduledFrameResult() const {
     if (mHandler->isFramePending()) {
-        return Clock::now();
+        return scheduler::ScheduleResult{TimePoint::now(), mHandler->getExpectedVsyncTime()};
     }
-
     std::lock_guard lock(mVsync.mutex);
-    if (const auto time = mVsync.scheduledFrameTime) {
-        return Clock::time_point(std::chrono::nanoseconds(*time));
+    if (const auto scheduledFrameTimeline = mVsync.scheduledFrameTimeOpt) {
+        return scheduledFrameTimeline;
     }
-
     return std::nullopt;
 }
 
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index edb424b..c5fc371 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -76,8 +76,7 @@
     virtual void scheduleConfigure() = 0;
     virtual void scheduleFrame() = 0;
 
-    using Clock = std::chrono::steady_clock;
-    virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
+    virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
 };
 
 namespace impl {
@@ -95,7 +94,9 @@
         explicit Handler(MessageQueue& queue) : mQueue(queue) {}
         void handleMessage(const Message& message) override;
 
-        bool isFramePending() const;
+        virtual TimePoint getExpectedVsyncTime() const { return mExpectedVsyncTime.load(); }
+
+        virtual bool isFramePending() const;
 
         virtual void dispatchFrame(VsyncId, TimePoint expectedVsyncTime);
     };
@@ -124,7 +125,7 @@
         TracedOrdinal<std::chrono::nanoseconds> workDuration
                 GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
         TimePoint lastCallbackTime GUARDED_BY(mutex);
-        std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
+        std::optional<scheduler::ScheduleResult> scheduledFrameTimeOpt GUARDED_BY(mutex);
         TracedOrdinal<int> value = {"VSYNC-sf", 0};
     };
 
@@ -150,7 +151,7 @@
     void scheduleConfigure() override;
     void scheduleFrame() override;
 
-    std::optional<Clock::time_point> getScheduledFrameTime() const override;
+    std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9f29e9f..d1934f2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -133,9 +133,9 @@
 
     void initVsync(frametimeline::TokenManager&, std::chrono::nanoseconds workDuration);
 
-    using Impl::getScheduledFrameTime;
     using Impl::setDuration;
 
+    using Impl::getScheduledFrameResult;
     using Impl::scheduleConfigure;
     using Impl::scheduleFrame;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index ed8f8fe..0c43ffb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -21,11 +21,15 @@
 #include <string>
 
 #include <ftl/mixins.h>
+#include <scheduler/Time.h>
 #include <utils/Timers.h>
 
 namespace android::scheduler {
 
-using ScheduleResult = std::optional<nsecs_t>;
+struct ScheduleResult {
+    TimePoint callbackTime;
+    TimePoint vsyncTime;
+};
 
 enum class CancelResult { Cancelled, TooLate, Error };
 
@@ -124,10 +128,12 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     The expected callback time if a callback was scheduled.
+     * \return                     The expected callback time if a callback was scheduled,
+     *                             along with VSYNC time for the callback scheduled.
      *                             std::nullopt if the callback is not registered.
      */
-    virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+    virtual std::optional<ScheduleResult> schedule(CallbackToken token,
+                                                   ScheduleTiming scheduleTiming) = 0;
 
     /*
      * Update the timing information for a scheduled callback.
@@ -135,10 +141,12 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     The expected callback time if a callback was scheduled.
+     * \return                     The expected callback time if a callback was scheduled,
+     *                             along with VSYNC time for the callback scheduled.
      *                             std::nullopt if the callback is not registered.
      */
-    virtual ScheduleResult update(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
+    virtual std::optional<ScheduleResult> update(CallbackToken token,
+                                                 ScheduleTiming scheduleTiming) = 0;
 
     /* Cancels a scheduled callback, if possible.
      *
@@ -168,10 +176,10 @@
     VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
 
     // See documentation for VSyncDispatch::schedule.
-    ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
+    std::optional<ScheduleResult> schedule(VSyncDispatch::ScheduleTiming scheduleTiming);
 
     // See documentation for VSyncDispatch::update.
-    ScheduleResult update(VSyncDispatch::ScheduleTiming scheduleTiming);
+    std::optional<ScheduleResult> update(VSyncDispatch::ScheduleTiming scheduleTiming);
 
     // See documentation for VSyncDispatch::cancel.
     CancelResult cancel();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index b92fa24..2a3e5b6 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -38,9 +38,10 @@
 
 namespace {
 
-nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
-                                const VSyncDispatch::ScheduleTiming& timing) {
-    return nextVsyncTime - timing.readyDuration - timing.workDuration;
+ScheduleResult getExpectedCallbackTime(nsecs_t nextVsyncTime,
+                                       const VSyncDispatch::ScheduleTiming& timing) {
+    return {TimePoint::fromNs(nextVsyncTime - timing.readyDuration - timing.workDuration),
+            TimePoint::fromNs(nextVsyncTime)};
 }
 
 } // namespace
@@ -84,8 +85,8 @@
     return {mArmedInfo->mActualVsyncTime};
 }
 
-ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
-                                                      VSyncTracker& tracker, nsecs_t now) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueueEntry::schedule(
+        VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) {
     auto nextVsyncTime =
             tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
                                                           now + timing.workDuration +
@@ -115,14 +116,15 @@
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return nextWakeupTime;
+    return ScheduleResult{TimePoint::fromNs(nextWakeupTime), TimePoint::fromNs(nextVsyncTime)};
 }
 
-nsecs_t VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
+ScheduleResult VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(
         VSyncTracker& tracker, nsecs_t now, VSyncDispatch::ScheduleTiming timing) {
     mWorkloadUpdateInfo = timing;
     const auto armedInfo = update(tracker, now, timing, mArmedInfo);
-    return armedInfo.mActualWakeupTime;
+    return {TimePoint::fromNs(armedInfo.mActualWakeupTime),
+            TimePoint::fromNs(armedInfo.mActualVsyncTime)};
 }
 
 bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
@@ -383,14 +385,14 @@
     }
 }
 
-ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
-                                                 ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::schedule(CallbackToken token,
+                                                                ScheduleTiming scheduleTiming) {
     std::lock_guard lock(mMutex);
     return scheduleLocked(token, scheduleTiming);
 }
 
-ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token,
-                                                       ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::scheduleLocked(
+        CallbackToken token, ScheduleTiming scheduleTiming) {
     auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
         return {};
@@ -405,19 +407,20 @@
         return callback->addPendingWorkloadUpdate(*mTracker, now, scheduleTiming);
     }
 
-    const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now);
-    if (!result.has_value()) {
+    const auto resultOpt = callback->schedule(scheduleTiming, *mTracker, now);
+
+    if (!resultOpt) {
         return {};
     }
-
     if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
         rearmTimerSkippingUpdateFor(now, it);
     }
 
-    return result;
+    return resultOpt;
 }
 
-ScheduleResult VSyncDispatchTimerQueue::update(CallbackToken token, ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncDispatchTimerQueue::update(CallbackToken token,
+                                                              ScheduleTiming scheduleTiming) {
     std::lock_guard lock(mMutex);
     const auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
@@ -494,14 +497,16 @@
     if (mToken) mDispatch->unregisterCallback(*mToken);
 }
 
-ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::schedule(
+        VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mToken) {
         return std::nullopt;
     }
     return mDispatch->schedule(*mToken, scheduleTiming);
 }
 
-ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) {
+std::optional<ScheduleResult> VSyncCallbackRegistration::update(
+        VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mToken) {
         return std::nullopt;
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index b5ddd25..afb67ae 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -48,7 +48,8 @@
     std::optional<nsecs_t> lastExecutedVsyncTarget() const;
 
     // This moves the state from disarmed->armed and will calculate the wakeupTime.
-    ScheduleResult schedule(VSyncDispatch::ScheduleTiming, VSyncTracker&, nsecs_t now);
+    std::optional<ScheduleResult> schedule(VSyncDispatch::ScheduleTiming, VSyncTracker&,
+                                           nsecs_t now);
     // This will update armed entries with the latest vsync information. Entry remains armed.
     void update(VSyncTracker&, nsecs_t now);
 
@@ -69,7 +70,8 @@
 
     // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
     // call to update()
-    nsecs_t addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now, VSyncDispatch::ScheduleTiming);
+    ScheduleResult addPendingWorkloadUpdate(VSyncTracker&, nsecs_t now,
+                                            VSyncDispatch::ScheduleTiming);
 
     // Checks if there is a pending update to the workload, returning true if so.
     bool hasPendingWorkloadUpdate() const;
@@ -128,8 +130,8 @@
 
     CallbackToken registerCallback(Callback, std::string callbackName) final;
     void unregisterCallback(CallbackToken) final;
-    ScheduleResult schedule(CallbackToken, ScheduleTiming) final;
-    ScheduleResult update(CallbackToken, ScheduleTiming) final;
+    std::optional<ScheduleResult> schedule(CallbackToken, ScheduleTiming) final;
+    std::optional<ScheduleResult> update(CallbackToken, ScheduleTiming) final;
     CancelResult cancel(CallbackToken) final;
     void dump(std::string&) const final;
 
@@ -147,7 +149,7 @@
     void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate)
             REQUIRES(mMutex);
     void cancelTimer() REQUIRES(mMutex);
-    ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
+    std::optional<ScheduleResult> scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
 
     std::mutex mutable mMutex;
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index edba50b..0436c3c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2759,7 +2759,11 @@
     // TODO(b/255601557) Update frameInterval per display
     refreshArgs.frameInterval =
             mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime());
-    refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
+    const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+    const auto scheduledFrameTimeOpt = scheduledFrameResultOpt
+            ? std::optional{scheduledFrameResultOpt->callbackTime}
+            : std::nullopt;
+    refreshArgs.scheduledFrameTime = scheduledFrameTimeOpt;
     refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0;
     // Store the present time just before calling to the composition engine so we could notify
     // the scheduler.
@@ -4258,7 +4262,8 @@
 
     auto hintStatus = data.hintStatus.load();
     if (!expectedPresentWithinTimeout) {
-        if (hintStatus != NotifyExpectedPresentHintStatus::Sent ||
+        if ((hintStatus != NotifyExpectedPresentHintStatus::Sent &&
+             hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnTx) ||
             (timeoutOpt && timeoutOpt->ns() == 0)) {
             // Send the hint immediately if timeout, as the hint gets
             // delayed otherwise, as the frame is scheduled close
@@ -4267,11 +4272,16 @@
                         .compare_exchange_strong(hintStatus,
                                                  NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
                 scheduleNotifyExpectedPresentHint(displayId);
+                return;
             }
-            return;
         }
     }
 
+    if (hintStatus == NotifyExpectedPresentHintStatus::Sent &&
+        data.hintStatus.compare_exchange_strong(hintStatus,
+                                                NotifyExpectedPresentHintStatus::ScheduleOnTx)) {
+        return;
+    }
     if (hintStatus != NotifyExpectedPresentHintStatus::Start) {
         return;
     }
@@ -4279,7 +4289,8 @@
     mScheduler->scheduleFrame();
 }
 
-void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
+void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+                                                       VsyncId vsyncId) {
     auto itr = mNotifyExpectedPresentMap.find(displayId);
     if (itr == mNotifyExpectedPresentMap.end()) {
         return;
@@ -4288,10 +4299,30 @@
     const char* const whence = __func__;
     const auto sendHint = [=, this]() {
         auto& data = mNotifyExpectedPresentMap.at(displayId);
-        data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent);
-        const auto status =
-                getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp,
-                                                      data.lastFrameInterval);
+        TimePoint expectedPresentTime = data.lastExpectedPresentTimestamp;
+        if (ftl::to_underlying(vsyncId) != FrameTimelineInfo::INVALID_VSYNC_ID) {
+            const auto predictionOpt = mFrameTimeline->getTokenManager()->getPredictionsForToken(
+                    ftl::to_underlying(vsyncId));
+            const auto expectedPresentTimeOnPredictor = TimePoint::fromNs(
+                    predictionOpt ? predictionOpt->presentTime : expectedPresentTime.ns());
+            const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+            const auto expectedPresentTimeOnScheduler = scheduledFrameResultOpt.has_value()
+                    ? scheduledFrameResultOpt->vsyncTime
+                    : TimePoint::fromNs(0);
+            expectedPresentTime =
+                    std::max(expectedPresentTimeOnPredictor, expectedPresentTimeOnScheduler);
+        }
+
+        if (expectedPresentTime < TimePoint::now()) {
+            expectedPresentTime =
+                    mScheduler->getVsyncSchedule()->vsyncDeadlineAfter(TimePoint::now());
+            if (mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration >
+                mScheduler->getVsyncSchedule(displayId)->period()) {
+                expectedPresentTime += mScheduler->getVsyncSchedule(displayId)->period();
+            }
+        }
+        const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime,
+                                                                  data.lastFrameInterval);
         if (status != NO_ERROR) {
             ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence,
                   displayId.value);
@@ -4309,7 +4340,11 @@
             }
         }));
     }
-    sendHint();
+    auto scheduleHintOnPresent = NotifyExpectedPresentHintStatus::ScheduleOnPresent;
+    if (itr->second.hintStatus.compare_exchange_strong(scheduleHintOnPresent,
+                                                       NotifyExpectedPresentHintStatus::Sent)) {
+        sendHint();
+    }
 }
 
 void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) {
@@ -5112,6 +5147,11 @@
 
     const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
     mTransactionHandler.queueTransaction(std::move(state));
+    for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
+        if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
+            scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
+        }
+    }
     setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
     return NO_ERROR;
 }
@@ -8820,7 +8860,11 @@
         return;
     }
 
-    mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
+    const auto scheduledFrameResultOpt = mScheduler->getScheduledFrameResult();
+    const auto scheduleFrameTimeOpt = scheduledFrameResultOpt
+            ? std::optional{scheduledFrameResultOpt->callbackTime}
+            : std::nullopt;
+    mRegionSamplingThread->onCompositionComplete(scheduleFrameTimeOpt);
 }
 
 void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDisplay) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index be05797..ce9fa15 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1500,7 +1500,9 @@
     std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap;
     void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override
             REQUIRES(kMainThreadContext);
-    void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId);
+    void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId,
+                                           VsyncId vsyncId = VsyncId{
+                                                   FrameTimelineInfo::INVALID_VSYNC_ID});
     void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod,
                                          TimePoint expectedPresentTime, Fps frameInterval,
                                          std::optional<Period> timeoutOpt);
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index d5ec654..3eabe1f 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -117,14 +117,16 @@
         mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp);
     }
 
+    static constexpr scheduler::ScheduleResult kScheduleResult{TimePoint::fromNs(0),
+                                                               TimePoint::fromNs(0)};
     AsyncCallRecorderWithCannedReturn<
             scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
                                           scheduler::VSyncDispatch::ScheduleTiming)>
-            mVSyncCallbackScheduleRecorder{0};
+            mVSyncCallbackScheduleRecorder{kScheduleResult};
     AsyncCallRecorderWithCannedReturn<
             scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
                                           scheduler::VSyncDispatch::ScheduleTiming)>
-            mVSyncCallbackUpdateRecorder{0};
+            mVSyncCallbackUpdateRecorder{kScheduleResult};
     AsyncCallRecorderWithCannedReturn<
             scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback,
                                                         std::string)>
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index f5661fc..71f9f88 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -25,6 +25,7 @@
 #include "FrameTimeline.h"
 #include "Scheduler/MessageQueue.h"
 #include "mock/MockVSyncDispatch.h"
+#include "utils/Timers.h"
 
 namespace android {
 
@@ -49,6 +50,8 @@
         using MessageQueue::Handler::Handler;
 
         MOCK_METHOD(void, dispatchFrame, (VsyncId, TimePoint), (override));
+        MOCK_METHOD(bool, isFramePending, (), (const, override));
+        MOCK_METHOD(TimePoint, getExpectedVsyncTime, (), (const override));
     };
 
     explicit TestableMessageQueue(sp<MockHandler> handler)
@@ -94,13 +97,17 @@
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
-    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    const auto timePoint = TimePoint::fromNs(1234);
+    const auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 }
 
 TEST_F(MessageQueueTest, commitTwice) {
@@ -109,17 +116,25 @@
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    auto timePoint = TimePoint::fromNs(1234);
+    auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
+    timePoint = TimePoint::fromNs(4567);
+    scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(4567, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(4567, scheduledFrameResult->vsyncTime.ns());
 }
 
 TEST_F(MessageQueueTest, commitTwiceWithCallback) {
@@ -128,11 +143,15 @@
                                                                  .readyDuration = 0,
                                                                  .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
+    const auto timePoint = TimePoint::fromNs(1234);
+    auto scheduleResult = scheduler::ScheduleResult{timePoint, timePoint};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 
-    ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
-    EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
+    auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_EQ(1234, scheduledFrameResult->callbackTime.ns());
+    EXPECT_EQ(1234, scheduledFrameResult->vsyncTime.ns());
 
     constexpr TimePoint kStartTime = TimePoint::fromNs(100);
     constexpr TimePoint kEndTime = kStartTime + kDuration;
@@ -148,14 +167,15 @@
     EXPECT_NO_FATAL_FAILURE(
             mEventQueue.vsyncCallback(kPresentTime.ns(), kStartTime.ns(), kEndTime.ns()));
 
-    EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
+    EXPECT_FALSE(mEventQueue.getScheduledFrameResult());
 
     const auto timingAfterCallback =
             scheduler::VSyncDispatch::ScheduleTiming{.workDuration = kDuration.ns(),
                                                      .readyDuration = 0,
                                                      .lastVsync = kPresentTime.ns()};
-
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
+    scheduleResult = scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback))
+            .WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
@@ -167,9 +187,24 @@
                                                      .readyDuration = 0,
                                                      .lastVsync = 0};
 
-    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
+    const auto scheduleResult =
+            scheduler::ScheduleResult{TimePoint::fromNs(0), TimePoint::fromNs(0)};
+    EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(scheduleResult));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
 }
 
+TEST_F(MessageQueueTest, scheduleResultWhenFrameIsPending) {
+    const auto timePoint = TimePoint::now();
+    EXPECT_CALL(*mEventQueue.mHandler, isFramePending()).WillOnce(Return(true));
+    EXPECT_CALL(*mEventQueue.mHandler, getExpectedVsyncTime()).WillRepeatedly(Return(timePoint));
+
+    const auto scheduledFrameResult = mEventQueue.getScheduledFrameResult();
+
+    ASSERT_TRUE(scheduledFrameResult);
+    EXPECT_NEAR(static_cast<double>(TimePoint::now().ns()),
+                static_cast<double>(scheduledFrameResult->callbackTime.ns()), ms2ns(1));
+    EXPECT_EQ(timePoint, scheduledFrameResult->vsyncTime);
+}
+
 } // namespace
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 91b9018..20a3315 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -41,6 +41,33 @@
     }
 
 protected:
+    void setTransactionState() {
+        ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
+        TransactionInfo transaction;
+        mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
+                                     transaction.flags, transaction.applyToken,
+                                     transaction.inputWindowCommands,
+                                     TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
+                                     transaction.unCachedBuffers,
+                                     /*HasListenerCallbacks=*/false, transaction.callbacks,
+                                     transaction.id, transaction.mergedTransactionIds);
+    }
+
+    struct TransactionInfo {
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags = 0;
+        sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+        InputWindowCommands inputWindowCommands;
+        int64_t desiredPresentTime = 0;
+        bool isAutoTimestamp = false;
+        FrameTimelineInfo frameTimelineInfo{};
+        std::vector<client_cache_t> unCachedBuffers;
+        uint64_t id = static_cast<uint64_t>(-1);
+        std::vector<uint64_t> mergedTransactionIds;
+        std::vector<ListenerCallbacks> callbacks;
+    };
+
     struct Compositor final : ICompositor {
         explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger)
               : displayId(displayId), surfaceFlinger(surfaceFlinger) {}
@@ -102,7 +129,7 @@
 };
 
 TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     ASSERT_NO_FATAL_FAILURE(
             mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId,
                                                   TimePoint::fromNs(expectedPresentTime),
@@ -120,7 +147,7 @@
 }
 
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     {
         // Very first ExpectedPresent after idle, no previous timestamp.
         EXPECT_CALL(*mComposer,
@@ -139,6 +166,10 @@
     {
         mCompositor->committed = false;
         expectedPresentTime += kFrameInterval60HzNs;
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         EXPECT_CALL(*mComposer,
                     notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(Error::NONE));
@@ -154,6 +185,10 @@
     }
     {
         expectedPresentTime += kFrameInterval60HzNs;
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         EXPECT_CALL(*mComposer,
                     notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
                 .WillOnce(Return(Error::NONE));
@@ -168,9 +203,8 @@
         ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
     }
 }
-
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) {
-    auto expectedPresentTime = systemTime() + ms2ns(10);
+    auto expectedPresentTime = TimePoint::now().ns() + ms2ns(10);
     {
         // Very first ExpectedPresent after idle, no previous timestamp
         mCompositor->committed = false;
@@ -185,6 +219,27 @@
         ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
     }
     {
+        EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0);
+        expectedPresentTime += 2 * kFrameInterval5HzNs;
+        mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod,
+                                                 TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+                                                 kTimeoutNs);
+        EXPECT_TRUE(
+                mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+        mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
+        {
+            EXPECT_CALL(*mComposer,
+                        notifyExpectedPresent(kHwcDisplayId, expectedPresentTime,
+                                              kFrameInterval60HzNs))
+                    .WillOnce(Return(Error::NONE));
+            // Hint sent with the setTransactionState
+            setTransactionState();
+            ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+        }
+    }
+    {
         // ExpectedPresentTime is after the timeoutNs
         mCompositor->committed = true;
         expectedPresentTime += 2 * kFrameInterval5HzNs;
@@ -194,7 +249,7 @@
                                                  kTimeoutNs);
         EXPECT_TRUE(
                 mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime));
-        ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId));
+        ASSERT_TRUE(mFlinger.verifyHintStatusIsScheduledOnTx(mPhysicalDisplayId));
         mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42});
         // Present happens notifyExpectedPresentHintStatus is Start
         ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId));
@@ -259,7 +314,7 @@
 }
 
 TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
-    const auto now = systemTime();
+    const auto now = TimePoint::now().ns();
     auto expectedPresentTime = now;
     static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs());
 
@@ -298,6 +353,10 @@
                                                  TimePoint::fromNs(expectedPresentTime),
                                                  Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs);
 
+        EXPECT_CALL(static_cast<mock::VSyncTracker&>(
+                            mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
+                    nextAnticipatedVSyncTimeFrom(_, _))
+                .WillRepeatedly(Return(expectedPresentTime));
         if (callNotifyExpectedPresentHint) {
             mCompositor->committed = false;
             ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId))
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index eb4e84e..951faf6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -247,7 +247,8 @@
                 mDispatch->schedule(cb,
                                     {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
         EXPECT_TRUE(result.has_value());
-        EXPECT_EQ(900, *result);
+        EXPECT_EQ(900, result->callbackTime.ns());
+        EXPECT_EQ(1000, result->vsyncTime.ns());
     }
 }
 
@@ -260,7 +261,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 
@@ -279,12 +281,14 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result =
             mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(700, *result);
+    EXPECT_EQ(700, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 
@@ -332,7 +336,8 @@
                                              .readyDuration = 0,
                                              .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod, *result);
+    EXPECT_EQ(mPeriod, result->callbackTime.ns());
+    EXPECT_EQ(workDuration + mPeriod, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -344,7 +349,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
     EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled);
 }
 
@@ -357,7 +363,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate);
 }
@@ -371,7 +378,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -392,7 +400,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 100, .readyDuration = 0, .lastVsync = mPeriod});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(mPeriod - 100, *result);
+    EXPECT_EQ(mPeriod - 100, result->callbackTime.ns());
+    EXPECT_EQ(mPeriod, result->vsyncTime.ns());
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -625,19 +634,22 @@
                                                    .readyDuration = 0,
                                                    .lastVsync = timestamp - mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 result = mDispatch->schedule(tmp,
                                              {.workDuration = 400,
                                               .readyDuration = 0,
                                               .lastVsync = timestamp});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 result = mDispatch->schedule(tmp,
                                              {.workDuration = 400,
                                               .readyDuration = 0,
                                               .lastVsync = timestamp + mVsyncMoveThreshold});
                 EXPECT_TRUE(result.has_value());
-                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                EXPECT_EQ(mPeriod + timestamp - 400, result->callbackTime.ns());
+                EXPECT_EQ(mPeriod + timestamp, result->vsyncTime.ns());
                 lastTarget = timestamp;
             },
             "oo");
@@ -726,10 +738,12 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 }
 
 // b/1450138150
@@ -741,12 +755,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1200, *result);
+    EXPECT_EQ(1200, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -763,12 +779,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(400, *result);
+    EXPECT_EQ(400, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -784,11 +802,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(400);
     result = mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(602, *result);
+    EXPECT_EQ(602, result->callbackTime.ns());
+    EXPECT_EQ(1002, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -796,12 +816,14 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
     result =
             mDispatch->schedule(cb0, {.workDuration = 1100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -812,12 +834,14 @@
     auto result =
             mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
     result =
             mDispatch->schedule(cb0, {.workDuration = 1900, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1100, *result);
+    EXPECT_EQ(1100, result->callbackTime.ns());
+    EXPECT_EQ(3000, result->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
@@ -829,11 +853,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 }
@@ -849,11 +875,13 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     result = mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(0, *result);
+    EXPECT_EQ(0, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
 }
@@ -899,14 +927,16 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -927,14 +957,16 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1630, *result);
+    EXPECT_EQ(1630, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -954,14 +986,16 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
     result = mDispatch->schedule(cb, {.workDuration = 370, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(80);
 
     ASSERT_EQ(1, cb.mCalls.size());
@@ -982,10 +1016,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -1009,10 +1045,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 100, .readyDuration = 0, .lastVsync = 2000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1900, *result);
+    EXPECT_EQ(1900, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -1045,10 +1083,12 @@
     auto result =
             mDispatch->schedule(cb1, {.workDuration = 400, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(600, *result);
+    EXPECT_EQ(600, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     result = mDispatch->schedule(cb2, {.workDuration = 390, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(610, *result);
+    EXPECT_EQ(610, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(700);
@@ -1072,7 +1112,8 @@
             mDispatch->schedule(cb,
                                 {.workDuration = 70, .readyDuration = 30, .lastVsync = intended});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(900, *result);
+    EXPECT_EQ(900, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1138,12 +1179,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(300);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(1200, *result);
+    EXPECT_EQ(1200, result->callbackTime.ns());
+    EXPECT_EQ(2000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1159,12 +1202,14 @@
     auto result =
             mDispatch->schedule(cb, {.workDuration = 500, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(500, *result);
+    EXPECT_EQ(500, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
     mMockClock.advanceBy(300);
 
     result = mDispatch->schedule(cb, {.workDuration = 800, .readyDuration = 0, .lastVsync = 1000});
     EXPECT_TRUE(result.has_value());
-    EXPECT_EQ(300, *result);
+    EXPECT_EQ(300, result->callbackTime.ns());
+    EXPECT_EQ(1000, result->vsyncTime.ns());
 
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -1197,9 +1242,12 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResultOpt =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResultOpt->vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1220,9 +1268,12 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
-                               *mStubTracker.get(), now)
-                        .has_value());
+    const auto scheduleResultOpt =
+            entry.schedule({.workDuration = 500, .readyDuration = 0, .lastVsync = 994},
+                           *mStubTracker, now);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(9500, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(10000, scheduleResultOpt->vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -1243,9 +1294,12 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResultOpt =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResultOpt->vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1275,17 +1329,20 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResultOpt =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResultOpt->vsyncTime.ns());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
 
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(920));
@@ -1307,26 +1364,35 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    auto scheduleResultOpt =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResultOpt->vsyncTime.ns());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResultOpt = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+                                       *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(1800, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResultOpt->vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResultOpt = entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
+                                       *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(1950, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResultOpt->vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResultOpt = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 1001},
+                                       *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(1800, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResultOpt->vsyncTime.ns());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
@@ -1349,31 +1415,36 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    auto scheduleResultOpt =
+            entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(1000, scheduleResultOpt->vsyncTime.ns());
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    scheduleResultOpt = entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
+                                       *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(1800, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(2000, scheduleResultOpt->vsyncTime.ns());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
     EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
+                               *mStubTracker, 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
+                               *mStubTracker, 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
+                               *mStubTracker, 0)
                         .has_value());
     EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
+                               *mStubTracker, 0)
                         .has_value());
 }
 
@@ -1389,7 +1460,7 @@
                                     .readyDuration = 0,
                                     .lastVsync = 400});
     EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
-    entry.update(*mStubTracker.get(), 0);
+    entry.update(*mStubTracker, 0);
     EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
     EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
 }
@@ -1409,9 +1480,12 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
-                               *mStubTracker.get(), 0)
-                        .has_value());
+    const auto scheduleResultOpt =
+            entry.schedule({.workDuration = 70, .readyDuration = 30, .lastVsync = 500},
+                           *mStubTracker, 0);
+    ASSERT_TRUE(scheduleResultOpt);
+    EXPECT_EQ(900, scheduleResultOpt->callbackTime.ns());
+    EXPECT_EQ(mPeriod, scheduleResultOpt->vsyncTime.ns());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
index dc32ff9..1dc2ef4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
@@ -29,8 +29,10 @@
 
     MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
     MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(scheduler::ScheduleResult, update, (CallbackToken, ScheduleTiming), (override));
+    MOCK_METHOD(std::optional<scheduler::ScheduleResult>, schedule, (CallbackToken, ScheduleTiming),
+                (override));
+    MOCK_METHOD(std::optional<scheduler::ScheduleResult>, update, (CallbackToken, ScheduleTiming),
+                (override));
     MOCK_METHOD(scheduler::CancelResult, cancel, (CallbackToken token), (override));
     MOCK_METHOD(void, dump, (std::string&), (const, override));
 };