SF: return the expected wakeup time from VSyncDispatch

Return the expected wakeup of a callback scheduled with VSyncDispatch.
This would help the client to better plan it's work. In particular
this would be used for SF to know when to schedule the region sample
thread to capture the screen.

Bug: 181983990
Test: SF unit tests
Change-Id: I38b538c1f6fac7cadcf456c55e78f67cf637480d
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index ce5c31a..50b38c9 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -60,8 +60,7 @@
                 mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                         .readyDuration = mReadyDuration.count(),
                                         .earliestVsync = mLastCallTime.count()});
-        LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
-                            "Error scheduling callback: rc %X", scheduleResult);
+        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
     }
 
     void stop() {
@@ -100,8 +99,7 @@
                     mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                             .readyDuration = mReadyDuration.count(),
                                             .earliestVsync = vsyncTime});
-            LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
-                                "Error rescheduling callback: rc %X", scheduleResult);
+            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
         }
     }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 9d71103..b52706f 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,8 +16,10 @@
 
 #pragma once
 
+#include <utils/Log.h>
 #include <utils/Timers.h>
 #include <functional>
+#include <optional>
 #include <string>
 
 #include "StrongTyping.h"
@@ -26,7 +28,8 @@
 class TimeKeeper;
 class VSyncTracker;
 
-enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+using ScheduleResult = std::optional<nsecs_t>;
+
 enum class CancelResult { Cancelled, TooLate, Error };
 
 /*
@@ -121,11 +124,8 @@
      *
      * \param [in] token           The callback to schedule.
      * \param [in] scheduleTiming  The timing information for this schedule call
-     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
-     *                             A ScheduleResult::CannotSchedule
-     *                             if (workDuration + readyDuration - earliestVsync) is in the past,
-     * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if
-     * there was another error.
+     * \return                     The expected callback time if a callback was scheduled.
+     *                             std::nullopt if the callback is not registered.
      */
     virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index ca6ea27..28be962 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -26,6 +26,20 @@
 namespace android::scheduler {
 using base::StringAppendF;
 
+namespace {
+nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    return nextVsyncTime - timing.readyDuration - timing.workDuration;
+}
+
+nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now,
+                                const VSyncDispatch::ScheduleTiming& timing) {
+    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
+            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
+    return getExpectedCallbackTime(nextVsyncTime, timing);
+}
+} // namespace
+
 VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
 TimeKeeper::~TimeKeeper() = default;
@@ -74,7 +88,7 @@
     bool const wouldSkipAVsyncTarget =
             mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
     if (wouldSkipAVsyncTarget) {
-        return ScheduleResult::Scheduled;
+        return getExpectedCallbackTime(nextVsyncTime, timing);
     }
 
     bool const alreadyDispatchedForVsync = mLastDispatchTime &&
@@ -89,7 +103,7 @@
     auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
     mScheduleTiming = timing;
     mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
-    return ScheduleResult::Scheduled;
+    return getExpectedCallbackTime(nextVsyncTime, timing);
 }
 
 void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) {
@@ -317,7 +331,7 @@
 
 ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                  ScheduleTiming scheduleTiming) {
-    auto result = ScheduleResult::Error;
+    ScheduleResult result;
     {
         std::lock_guard lock(mMutex);
 
@@ -333,11 +347,11 @@
         auto const rearmImminent = now > mIntendedWakeupTime;
         if (CC_UNLIKELY(rearmImminent)) {
             callback->addPendingWorkloadUpdate(scheduleTiming);
-            return ScheduleResult::Scheduled;
+            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
         }
 
         result = callback->schedule(scheduleTiming, mTracker, now);
-        if (result == ScheduleResult::CannotSchedule) {
+        if (!result.has_value()) {
             return result;
         }
 
@@ -416,7 +430,7 @@
 
 ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
     if (!mValidToken) {
-        return ScheduleResult::Error;
+        return std::nullopt;
     }
     return mDispatch.get().schedule(mToken, scheduleTiming);
 }
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 54f4c7c..a9ad249 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -66,7 +66,7 @@
             ALOGD("schedule: %zu", token.value());
             if (mCallbacks.count(token) == 0) {
                 ALOGD("schedule: callback %zu not registered", token.value());
-                return scheduler::ScheduleResult::Error;
+                return scheduler::ScheduleResult{};
             }
 
             auto& callback = mCallbacks.at(token);
@@ -75,7 +75,7 @@
             callback.targetWakeupTime =
                     timing.earliestVsync - timing.workDuration - timing.readyDuration;
             ALOGD("schedule: callback %zu scheduled", token.value());
-            return scheduler::ScheduleResult::Scheduled;
+            return scheduler::ScheduleResult{callback.targetWakeupTime};
         });
 
         ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 8208b3f..18ebbab 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -104,7 +104,7 @@
     const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
@@ -114,10 +114,10 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
@@ -127,7 +127,7 @@
                                                                  .readyDuration = 0,
                                                                  .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 
     const auto startTime = 100;
@@ -146,7 +146,7 @@
                                                      .readyDuration = 0,
                                                      .earliestVsync = presentTime};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
@@ -158,7 +158,7 @@
                                                      .readyDuration = 0,
                                                      .earliestVsync = 0};
 
-    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1);
+    EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
     EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index b64cce9..d59d64b 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -232,11 +232,12 @@
         VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
                                           mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
-        EXPECT_EQ(mDispatch.schedule(cb,
-                                     {.workDuration = 100,
-                                      .readyDuration = 0,
-                                      .earliestVsync = 1000}),
-                  ScheduleResult::Scheduled);
+        const auto result = mDispatch.schedule(cb,
+                                               {.workDuration = 100,
+                                                .readyDuration = 0,
+                                                .earliestVsync = 1000});
+        EXPECT_TRUE(result.has_value());
+        EXPECT_EQ(900, *result);
     }
 }
 
@@ -245,11 +246,13 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 100,
+                                            .readyDuration = 0,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
+
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -277,11 +280,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = workDuration,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = workDuration,
+                                            .readyDuration = 0,
+                                            .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
@@ -289,11 +293,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
 }
 
@@ -302,11 +306,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
     mMockClock.advanceBy(950);
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
 }
@@ -316,11 +320,11 @@
     EXPECT_CALL(mMockClock, alarmCancel());
 
     PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -337,11 +341,11 @@
 
     PausingCallback cb(mDispatch, 50ms);
     cb.stashResource(resource);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 100,
-                                  .readyDuration = 0,
-                                  .earliestVsync = mPeriod}),
-              ScheduleResult::Scheduled);
+    const auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(mPeriod - 100, *result);
 
     std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
     EXPECT_TRUE(cb.waitForPause());
@@ -535,21 +539,25 @@
     std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
             [&](auto timestamp, auto, auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp - mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp}),
-                          ScheduleResult::Scheduled);
-                EXPECT_EQ(mDispatch.schedule(tmp,
-                                             {.workDuration = 400,
-                                              .readyDuration = 0,
-                                              .earliestVsync = timestamp + mVsyncMoveThreshold}),
-                          ScheduleResult::Scheduled);
+                auto result =
+                        mDispatch.schedule(tmp,
+                                           {.workDuration = 400,
+                                            .readyDuration = 0,
+                                            .earliestVsync = timestamp - mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
+                result = mDispatch.schedule(tmp,
+                                            {.workDuration = 400,
+                                             .readyDuration = 0,
+                                             .earliestVsync = timestamp + mVsyncMoveThreshold});
+                EXPECT_TRUE(result.has_value());
+                EXPECT_EQ(mPeriod + timestamp - 400, *result);
                 lastTarget = timestamp;
             },
             "oo");
@@ -627,36 +635,41 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
-    EXPECT_THAT(mDispatch.schedule(token,
-                                   {.workDuration = 100,
-                                    .readyDuration = 0,
-                                    .earliestVsync = 1000}),
-                Eq(ScheduleResult::Error));
+    EXPECT_FALSE(mDispatch
+                         .schedule(token,
+                                   {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
+                         .has_value());
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 // b/1450138150
 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
     EXPECT_CALL(mMockClock, alarmAt(_, 500));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1200, *result);
     advanceToNextCallback();
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
 }
@@ -667,24 +680,30 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1002));
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     mMockClock.advanceBy(400);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(602, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
@@ -692,26 +711,32 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
     CountingCallback cb0(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb0,
+                               {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(500, *result);
     advanceToNextCallback();
-    EXPECT_EQ(mDispatch.schedule(cb0,
-                                 {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb0,
+                                {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1100, *result);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
     EXPECT_CALL(mMockClock, alarmAt(_, 600));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     advanceToNextCallback();
 }
@@ -754,16 +779,19 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb1.mCalls.size(), Eq(1));
@@ -779,16 +807,19 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
     CountingCallback cb(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
 
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    result = mDispatch.schedule(cb,
+                                {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1630, *result);
     mMockClock.advanceBy(80);
 
     EXPECT_THAT(cb.mCalls.size(), Eq(1));
@@ -802,12 +833,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -828,12 +862,15 @@
     CountingCallback cb1(mDispatch);
     CountingCallback cb2(mDispatch);
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(1900, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(620);
@@ -861,12 +898,15 @@
             .InSequence(seq)
             .WillOnce(Return(1000));
 
-    EXPECT_EQ(mDispatch.schedule(cb1,
-                                 {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb2,
-                                 {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}),
-              ScheduleResult::Scheduled);
+    auto result =
+            mDispatch.schedule(cb1,
+                               {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(600, *result);
+    result = mDispatch.schedule(cb2,
+                                {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(610, *result);
 
     mMockClock.setLag(100);
     mMockClock.advanceBy(700);
@@ -886,11 +926,12 @@
     EXPECT_CALL(mMockClock, alarmAt(_, 900));
 
     CountingCallback cb(mDispatch);
-    EXPECT_EQ(mDispatch.schedule(cb,
-                                 {.workDuration = 70,
-                                  .readyDuration = 30,
-                                  .earliestVsync = intended}),
-              ScheduleResult::Scheduled);
+    const auto result = mDispatch.schedule(cb,
+                                           {.workDuration = 70,
+                                            .readyDuration = 30,
+                                            .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
     advanceToNextCallback();
 
     ASSERT_THAT(cb.mCalls.size(), Eq(1));
@@ -922,9 +963,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -944,9 +985,9 @@
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
-    EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
-                               mStubTracker, now),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
+                               mStubTracker, now)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(9500));
@@ -967,9 +1008,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));
@@ -1002,9 +1043,9 @@
     entry.update(mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
@@ -1018,9 +1059,9 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.update(mStubTracker, 0);
 
     auto const wakeup = entry.wakeupTime();
@@ -1031,26 +1072,26 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     entry.executing(); // 1000 is executing
     // had 1000 not been executing, this could have been scheduled for time 800.
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
+                               mStubTracker, 0)
+                        .has_value());
     EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
     EXPECT_THAT(*entry.readyTime(), Eq(2000));
 }
@@ -1071,32 +1112,32 @@
             .InSequence(seq)
             .WillOnce(Return(2000));
 
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 
     entry.executing(); // 1000 is executing
 
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
     VSyncDispatchTimerQueueEntry entry(
             "test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
-    EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
-    EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
+    EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
@@ -1128,9 +1169,9 @@
             },
             mVsyncMoveThreshold);
 
-    EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
-                               mStubTracker, 0),
-                Eq(ScheduleResult::Scheduled));
+    EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
+                               mStubTracker, 0)
+                        .has_value());
     auto const wakeup = entry.wakeupTime();
     ASSERT_TRUE(wakeup);
     EXPECT_THAT(*wakeup, Eq(900));