SF: remove DispSyncSource

DispSyncSource is a legacy reminder of DispSync which was removed
a while ago. This changes removes the DispSyncSource and instead use
the VsyncDispatch interface directly.

Test: SF unit tests
Bug: 255601557
Bug: 256196556
Change-Id: I4247c3927b9ee9443437bf960f8cedc29759c4ac
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 6d1b3fe..824899b 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -36,6 +36,7 @@
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockTimeStats.cpp",
         "mock/MockVsyncController.cpp",
+        "mock/MockVSyncDispatch.cpp",
         "mock/MockVSyncTracker.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
@@ -71,7 +72,6 @@
         "libsurfaceflinger_unittest_main.cpp",
         "AidlPowerHalWrapperTest.cpp",
         "CompositionTest.cpp",
-        "DispSyncSourceTest.cpp",
         "DisplayIdGeneratorTest.cpp",
         "DisplayTransactionTest.cpp",
         "DisplayDevice_GetBestColorModeTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
deleted file mode 100644
index 67ace1a..0000000
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-#define LOG_NDEBUG 0
-
-#include <inttypes.h>
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <log/log.h>
-
-#include "AsyncCallRecorder.h"
-#include "Scheduler/DispSyncSource.h"
-#include "Scheduler/VSyncDispatch.h"
-#include "mock/MockVSyncTracker.h"
-
-namespace android {
-namespace {
-
-using namespace std::chrono_literals;
-using namespace testing;
-
-class MockVSyncDispatch : public scheduler::VSyncDispatch {
-public:
-    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::CancelResult, cancel, (CallbackToken), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-
-    MockVSyncDispatch() {
-        ON_CALL(*this, registerCallback)
-                .WillByDefault(
-                        [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback,
-                               std::string) {
-                            CallbackToken token(mNextToken);
-                            mNextToken++;
-
-                            mCallbacks.emplace(token, CallbackData(callback));
-                            ALOGD("registerCallback: %zu", token.value());
-                            return token;
-                        });
-
-        ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) {
-            ALOGD("unregisterCallback: %zu", token.value());
-            mCallbacks.erase(token);
-        });
-
-        ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) {
-            ALOGD("schedule: %zu", token.value());
-            if (mCallbacks.count(token) == 0) {
-                ALOGD("schedule: callback %zu not registered", token.value());
-                return scheduler::ScheduleResult{};
-            }
-
-            auto& callback = mCallbacks.at(token);
-            callback.scheduled = true;
-            callback.vsyncTime = timing.earliestVsync;
-            callback.targetWakeupTime =
-                    timing.earliestVsync - timing.workDuration - timing.readyDuration;
-            ALOGD("schedule: callback %zu scheduled", token.value());
-            return scheduler::ScheduleResult{callback.targetWakeupTime};
-        });
-
-        ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) {
-            ALOGD("cancel: %zu", token.value());
-            if (mCallbacks.count(token) == 0) {
-                ALOGD("cancel: callback %zu is not registered", token.value());
-                return scheduler::CancelResult::Error;
-            }
-
-            auto& callback = mCallbacks.at(token);
-            callback.scheduled = false;
-            ALOGD("cancel: callback %zu cancelled", token.value());
-            return scheduler::CancelResult::Cancelled;
-        });
-    }
-
-    void triggerCallbacks() {
-        ALOGD("triggerCallbacks");
-        for (auto& [token, callback] : mCallbacks) {
-            if (callback.scheduled) {
-                ALOGD("triggerCallbacks: callback %zu", token.value());
-                callback.scheduled = false;
-                callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime);
-            } else {
-                ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value());
-            }
-        }
-    }
-
-private:
-    struct CallbackData {
-        explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func)
-              : func(std::move(func)) {}
-
-        std::function<void(nsecs_t, nsecs_t, nsecs_t)> func;
-        bool scheduled = false;
-        nsecs_t vsyncTime = 0;
-        nsecs_t targetWakeupTime = 0;
-        nsecs_t readyTime = 0;
-    };
-
-    std::unordered_map<CallbackToken, CallbackData> mCallbacks;
-    size_t mNextToken;
-};
-
-class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback {
-protected:
-    DispSyncSourceTest();
-    ~DispSyncSourceTest() override;
-
-    void SetUp() override;
-    void createDispSyncSource();
-
-    void onVSyncEvent(nsecs_t when, VSyncSource::VSyncData) override;
-
-    std::unique_ptr<MockVSyncDispatch> mVSyncDispatch;
-    std::unique_ptr<mock::VSyncTracker> mVSyncTracker;
-    std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource;
-
-    AsyncCallRecorder<void (*)(nsecs_t, VSyncSource::VSyncData)> mVSyncEventCallRecorder;
-
-    static constexpr std::chrono::nanoseconds mWorkDuration = 20ms;
-    static constexpr std::chrono::nanoseconds mReadyDuration = 10ms;
-    static constexpr int mIterations = 100;
-    const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398};
-    const std::string mName = "DispSyncSourceTest";
-};
-
-DispSyncSourceTest::DispSyncSourceTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-DispSyncSourceTest::~DispSyncSourceTest() {
-    const ::testing::TestInfo* const test_info =
-            ::testing::UnitTest::GetInstance()->current_test_info();
-    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-void DispSyncSourceTest::SetUp() {
-    mVSyncDispatch = std::make_unique<MockVSyncDispatch>();
-    mVSyncTracker = std::make_unique<mock::VSyncTracker>();
-}
-
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when, VSyncSource::VSyncData vsyncData) {
-    ALOGD("onVSyncEvent: %" PRId64, when);
-
-    mVSyncEventCallRecorder.recordCall(when, vsyncData);
-}
-
-void DispSyncSourceTest::createDispSyncSource() {
-    mDispSyncSource = std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, *mVSyncTracker,
-                                                                  mWorkDuration, mReadyDuration,
-                                                                  true, mName.c_str());
-    mDispSyncSource->setCallback(this);
-}
-
-/* ------------------------------------------------------------------------
- * Test cases
- */
-
-TEST_F(DispSyncSourceTest, createDispSync) {
-    EXPECT_TRUE(mVSyncDispatch);
-}
-
-TEST_F(DispSyncSourceTest, createDispSyncSource) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken));
-    EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken))
-            .WillOnce(Return(scheduler::CancelResult::Cancelled));
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return());
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-}
-
-TEST_F(DispSyncSourceTest, noCallbackAfterInit) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    // DispSyncSource starts with Vsync disabled
-    mVSyncDispatch->triggerCallbacks();
-    EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value());
-}
-
-TEST_F(DispSyncSourceTest, waitForCallbacks) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(mIterations + 1);
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    mDispSyncSource->setVSyncEnabled(true);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when,
-                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
-                          mReadyDuration.count());
-    }
-}
-
-TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) {
-    InSequence seq;
-    EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(1);
-
-    createDispSyncSource();
-
-    EXPECT_TRUE(mDispSyncSource);
-
-    mDispSyncSource->setVSyncEnabled(true);
-    EXPECT_CALL(*mVSyncDispatch,
-                schedule(_, Truly([&](auto timings) {
-                             return timings.workDuration == mWorkDuration.count() &&
-                                     timings.readyDuration == mReadyDuration.count();
-                         })))
-            .Times(mIterations);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when,
-                  vsyncData.expectedPresentationTime - mWorkDuration.count() -
-                          mReadyDuration.count());
-    }
-
-    const auto newDuration = mWorkDuration / 2;
-    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
-                                              return timings.workDuration == newDuration.count() &&
-                                                      timings.readyDuration == 0;
-                                          })))
-            .Times(1);
-    mDispSyncSource->setDuration(newDuration, 0ns);
-
-    EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) {
-                                              return timings.workDuration == newDuration.count() &&
-                                                      timings.readyDuration == 0;
-                                          })))
-            .Times(mIterations);
-    for (int i = 0; i < mIterations; i++) {
-        mVSyncDispatch->triggerCallbacks();
-        const auto callbackData = mVSyncEventCallRecorder.waitForCall();
-        ASSERT_TRUE(callbackData.has_value());
-        const auto [when, vsyncData] = callbackData.value();
-        EXPECT_EQ(when, vsyncData.expectedPresentationTime - newDuration.count());
-    }
-
-    EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-    EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-}
-
-TEST_F(DispSyncSourceTest, getLatestVsyncData) {
-    const nsecs_t now = systemTime();
-    const nsecs_t expectedPresentationTime =
-            now + mWorkDuration.count() + mReadyDuration.count() + 1;
-    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
-            .WillOnce(Return(expectedPresentationTime));
-    {
-        InSequence seq;
-        EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1);
-        EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1);
-        EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1);
-    }
-
-    createDispSyncSource();
-    EXPECT_TRUE(mDispSyncSource);
-
-    const auto vsyncData = mDispSyncSource->getLatestVSyncData();
-    ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime);
-    EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index dd87f9d..b3aba37 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -30,12 +30,16 @@
 #include "DisplayHardware/DisplayMode.h"
 #include "FrameTimeline.h"
 #include "Scheduler/EventThread.h"
+#include "mock/MockVSyncDispatch.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
 
 using namespace std::chrono_literals;
 using namespace std::placeholders;
 
 using testing::_;
 using testing::Invoke;
+using testing::Return;
 
 namespace android {
 
@@ -50,24 +54,13 @@
 
 constexpr std::chrono::duration VSYNC_PERIOD(16ms);
 
-class MockVSyncSource : public VSyncSource {
-public:
-    const char* getName() const override { return "test"; }
-
-    MOCK_METHOD1(setVSyncEnabled, void(bool));
-    MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
-    MOCK_METHOD2(setDuration,
-                 void(std::chrono::nanoseconds workDuration,
-                      std::chrono::nanoseconds readyDuration));
-    MOCK_METHOD1(pauseVsyncCallback, void(bool));
-    MOCK_METHOD(VSyncSource::VSyncData, getLatestVSyncData, (), (const, override));
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
 } // namespace
 
 class EventThreadTest : public testing::Test {
 protected:
+    static constexpr std::chrono::nanoseconds kWorkDuration = 0ms;
+    static constexpr std::chrono::nanoseconds kReadyDuration = 3ms;
+
     class MockEventThreadConnection : public EventThreadConnection {
     public:
         MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
@@ -84,21 +77,21 @@
     EventThreadTest();
     ~EventThreadTest() override;
 
-    void createThread(std::unique_ptr<VSyncSource>);
+    void createThread();
     sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
                                                    EventRegistrationFlags eventRegistration = {},
                                                    uid_t ownerUid = mConnectionUid);
 
-    void expectVSyncSetEnabledCallReceived(bool expectedState);
+    void expectVSyncCallbackScheduleReceived(bool expectState);
     void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
                                             std::chrono::nanoseconds expectedReadyDuration);
-    VSyncSource::Callback* expectVSyncSetCallbackCallReceived();
     void expectVsyncEventReceivedByConnection(const char* name,
                                               ConnectionEventRecorder& connectionEventRecorder,
                                               nsecs_t expectedTimestamp, unsigned expectedCount);
     void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
-    void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
-                                               VSyncSource::VSyncData preferredVsyncData);
+    void expectVsyncEventFrameTimelinesCorrect(
+            nsecs_t expectedTimestamp,
+            /*VSyncSource::VSyncData*/ gui::VsyncEventData::FrameTimeline preferredVsyncData);
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -108,17 +101,31 @@
     void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                             std::vector<FrameRateOverride>);
 
-    AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
-    AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
-    AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)>
-            mVSyncSetDurationCallRecorder;
+    void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime,
+                      nsecs_t deadlineTimestamp) {
+        mThread->onVsync(expectedPresentationTime, timestamp, deadlineTimestamp);
+    }
+
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
+                                          scheduler::VSyncDispatch::ScheduleTiming)>
+            mVSyncCallbackScheduleRecorder{0};
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::ScheduleResult (*)(scheduler::VSyncDispatch::CallbackToken,
+                                          scheduler::VSyncDispatch::ScheduleTiming)>
+            mVSyncCallbackUpdateRecorder{0};
+    AsyncCallRecorderWithCannedReturn<
+            scheduler::VSyncDispatch::CallbackToken (*)(scheduler::VSyncDispatch::Callback,
+                                                        std::string)>
+            mVSyncCallbackRegisterRecorder{scheduler::VSyncDispatch::CallbackToken(0)};
+    AsyncCallRecorder<void (*)(scheduler::VSyncDispatch::CallbackToken)>
+            mVSyncCallbackUnregisterRecorder;
     AsyncCallRecorder<void (*)()> mResyncCallRecorder;
     AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
     ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
 
-    MockVSyncSource* mVSyncSource;
-    VSyncSource::Callback* mCallback = nullptr;
+    std::optional<scheduler::VsyncSchedule> mVsyncSchedule;
     std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
     sp<MockEventThreadConnection> mThrottledConnection;
@@ -133,19 +140,22 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    auto vsyncSource = std::make_unique<MockVSyncSource>();
-    mVSyncSource = vsyncSource.get();
+    mVsyncSchedule.emplace(scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(),
+                                                    std::make_unique<mock::VSyncDispatch>(),
+                                                    nullptr));
 
-    EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
-            .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
+    mock::VSyncDispatch& mockDispatch =
+            *static_cast<mock::VSyncDispatch*>(&mVsyncSchedule->getDispatch());
+    EXPECT_CALL(mockDispatch, registerCallback(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackRegisterRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, schedule(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackScheduleRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, update(_, _))
+            .WillRepeatedly(Invoke(mVSyncCallbackUpdateRecorder.getInvocable()));
+    EXPECT_CALL(mockDispatch, unregisterCallback(_))
+            .WillRepeatedly(Invoke(mVSyncCallbackUnregisterRecorder.getInvocable()));
 
-    EXPECT_CALL(*mVSyncSource, setCallback(_))
-            .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
-
-    EXPECT_CALL(*mVSyncSource, setDuration(_, _))
-            .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable()));
-
-    createThread(std::move(vsyncSource));
+    createThread();
     mConnection =
             createConnection(mConnectionEventCallRecorder,
                              gui::ISurfaceComposer::EventRegistration::modeChanged |
@@ -164,11 +174,12 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 
+    mThread.reset();
     // EventThread should unregister itself as VSyncSource callback.
-    EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
+    EXPECT_TRUE(mVSyncCallbackUnregisterRecorder.waitForCall().has_value());
 }
 
-void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+void EventThreadTest::createThread() {
     const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
         mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
         return (uid == mThrottledConnectionUid);
@@ -178,12 +189,13 @@
     };
 
     mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
-    mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
-                                                  throttleVsync, getVsyncPeriod);
+    mThread =
+            std::make_unique<impl::EventThread>(/*std::move(source), */ "EventThreadTest",
+                                                *mVsyncSchedule, mTokenManager.get(), throttleVsync,
+                                                getVsyncPeriod, kWorkDuration, kReadyDuration);
 
     // EventThread should register itself as VSyncSource callback.
-    mCallback = expectVSyncSetCallbackCallReceived();
-    ASSERT_TRUE(mCallback);
+    EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value());
 }
 
 sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
@@ -197,23 +209,20 @@
     return connection;
 }
 
-void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) {
-    auto args = mVSyncSetEnabledCallRecorder.waitForCall();
-    ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedState, std::get<0>(args.value()));
+void EventThreadTest::expectVSyncCallbackScheduleReceived(bool expectState) {
+    if (expectState) {
+        ASSERT_TRUE(mVSyncCallbackScheduleRecorder.waitForCall().has_value());
+    } else {
+        ASSERT_FALSE(mVSyncCallbackScheduleRecorder.waitForUnexpectedCall().has_value());
+    }
 }
 
 void EventThreadTest::expectVSyncSetDurationCallReceived(
         std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) {
-    auto args = mVSyncSetDurationCallRecorder.waitForCall();
+    auto args = mVSyncCallbackUpdateRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
-    EXPECT_EQ(expectedDuration, std::get<0>(args.value()));
-    EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value()));
-}
-
-VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() {
-    auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall();
-    return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr;
+    EXPECT_EQ(expectedDuration.count(), std::get<1>(args.value()).workDuration);
+    EXPECT_EQ(expectedReadyDuration.count(), std::get<1>(args.value()).readyDuration);
 }
 
 void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
@@ -246,7 +255,7 @@
 }
 
 void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(
-        nsecs_t expectedTimestamp, VSyncSource::VSyncData preferredVsyncData) {
+        nsecs_t expectedTimestamp, VsyncEventData::FrameTimeline preferredVsyncData) {
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
                                   << expectedTimestamp;
@@ -335,9 +344,10 @@
  */
 
 TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) {
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-    EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
-    EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackRegisterRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackScheduleRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackUpdateRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mVSyncCallbackUnregisterRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
@@ -350,7 +360,7 @@
     mThread->requestNextVsync(mConnection);
 
     // EventThread should not enable vsync callbacks.
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
@@ -360,47 +370,51 @@
     // EventThread should immediately request a resync.
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
-    // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    // EventThread should enable schedule a vsync callback
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a first vsync event.
     // The throttler should receive the event, as well as the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
+    // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback
+    expectVSyncCallbackScheduleReceived(true);
+
     // Use the received callback to signal a second vsync event.
     // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should also detect that at this point that it does not need
     // any more vsync events, and should disable their generation.
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection);
 
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a vsync event.
     // The throttler should receive the event, as well as the connection.
-    VSyncSource::VSyncData vsyncData = {456, 789};
-    mCallback->onVSyncEvent(123, vsyncData);
-    expectVsyncEventFrameTimelinesCorrect(123, vsyncData);
+    onVSyncEvent(123, 456, 789);
+    expectVsyncEventFrameTimelinesCorrect(123, {-1, 789, 456});
 }
 
 TEST_F(EventThreadTest, getLatestVsyncEventData) {
     const nsecs_t now = systemTime();
-    const nsecs_t preferredDeadline = now + 10000000;
     const nsecs_t preferredExpectedPresentationTime = now + 20000000;
-    const VSyncSource::VSyncData preferredData = {preferredExpectedPresentationTime,
-                                                  preferredDeadline};
-    EXPECT_CALL(*mVSyncSource, getLatestVSyncData()).WillOnce(Return(preferredData));
+    const nsecs_t preferredDeadline = preferredExpectedPresentationTime - kReadyDuration.count();
+
+    mock::VSyncTracker& mockTracker =
+            *static_cast<mock::VSyncTracker*>(&mVsyncSchedule->getTracker());
+    EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .WillOnce(Return(preferredExpectedPresentationTime));
 
     VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
 
@@ -451,8 +465,7 @@
     mThread->setVsyncRate(0, firstConnection);
 
     // By itself, this should not enable vsync events
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
-    EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value());
+    expectVSyncCallbackScheduleReceived(false);
 
     // However if there is another connection which wants events at a nonzero rate.....
     ConnectionEventRecorder secondConnectionEventRecorder{0};
@@ -461,12 +474,12 @@
     mThread->setVsyncRate(1, secondConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
     // the second connection. The first connection should not
     // get the event.
-    mCallback->onVSyncEvent(123, {456, 0});
+    onVSyncEvent(123, 0456, 0);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -476,21 +489,21 @@
     mThread->setVsyncRate(1, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Send a vsync event. EventThread should then make a call to the
     // throttler, and the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mConnectionUid);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     expectThrottleVsyncReceived(123, mConnectionUid);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    mCallback->onVSyncEvent(789, {777, 111});
+    onVSyncEvent(789, 777, 111);
     expectThrottleVsyncReceived(777, mConnectionUid);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -499,25 +512,25 @@
     mThread->setVsyncRate(2, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // The first event will not be seen by the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The second event will be seen by the connection.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     expectVsyncEventReceivedByConnection(456, 2u);
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The third event will not be seen by the connection.
-    mCallback->onVSyncEvent(789, {777, 744});
+    onVSyncEvent(789, 777, 744);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
     EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
 
     // The fourth event will be seen by the connection.
-    mCallback->onVSyncEvent(101112, {7847, 86});
+    onVSyncEvent(101112, 7847, 86);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
 
@@ -525,17 +538,17 @@
     mThread->setVsyncRate(1, mConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Destroy the only (strong) reference to the connection.
     mConnection = nullptr;
 
     // The first event will not be seen by the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 56, 789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) {
@@ -544,18 +557,22 @@
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // The first event will be seen by the connection, which then returns an error.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
+    // Another schedule is expected, since the connection is removed only after
+    // the next vsync is requested.
+    expectVSyncCallbackScheduleReceived(true);
+
     // A subsequent event will not be seen by the connection.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
     // EventThread should disable vsync callbacks with the second event
-    expectVSyncSetEnabledCallReceived(false);
+    expectVSyncCallbackScheduleReceived(false);
 }
 
 TEST_F(EventThreadTest, tracksEventConnections) {
@@ -571,10 +588,10 @@
     EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // The first event will be seen by the connection, which then returns an error.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
     expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
                                          1u);
@@ -587,19 +604,22 @@
     mThread->setVsyncRate(1, errorConnection);
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // The first event will be seen by the connection, which then returns a non-fatal error.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+    expectVSyncCallbackScheduleReceived(true);
 
     // A subsequent event will be seen by the connection, which still then returns a non-fatal
     // error.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
+    expectVSyncCallbackScheduleReceived(true);
 
     // EventThread will not disable vsync callbacks as the errors are non-fatal.
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    onVSyncEvent(456, 123, 0);
+    expectVSyncCallbackScheduleReceived(true);
 }
 
 TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) {
@@ -718,24 +738,27 @@
     EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
 
     // EventThread should enable vsync callbacks.
-    expectVSyncSetEnabledCallReceived(true);
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a first vsync event.
     // The throttler should receive the event, but not the connection.
-    mCallback->onVSyncEvent(123, {456, 789});
+    onVSyncEvent(123, 456, 789);
     expectThrottleVsyncReceived(456, mThrottledConnectionUid);
     mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+    expectVSyncCallbackScheduleReceived(true);
 
     // Use the received callback to signal a second vsync event.
     // The throttler should receive the event, but the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456, {123, 0});
+    onVSyncEvent(456, 123, 0);
     expectThrottleVsyncReceived(123, mThrottledConnectionUid);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+    expectVSyncCallbackScheduleReceived(true);
 
     // EventThread should not change the vsync state as it didn't send the event
     // yet
-    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+    onVSyncEvent(456, 123, 0);
+    expectVSyncCallbackScheduleReceived(true);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 5e1042e..7aa5201 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -23,6 +23,7 @@
 #include "FrameTimeline.h"
 #include "Scheduler/MessageQueue.h"
 #include "SurfaceFlinger.h"
+#include "mock/MockVSyncDispatch.h"
 
 namespace android {
 
@@ -59,14 +60,6 @@
     const sp<MockHandler> mHandler;
 };
 
-struct MockVSyncDispatch : scheduler::VSyncDispatch {
-    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::CancelResult, cancel, (CallbackToken token), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-};
-
 struct MockTokenManager : frametimeline::TokenManager {
     MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
     MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
@@ -79,7 +72,7 @@
         EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
     }
 
-    MockVSyncDispatch mVSyncDispatch;
+    mock::VSyncDispatch mVSyncDispatch;
     MockTokenManager mTokenManager;
     TestableMessageQueue mEventQueue;
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index b8a6063..0f53eb6 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -26,6 +26,7 @@
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
 #include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncDispatch.h"
 #include "mock/MockVSyncTracker.h"
 #include "mock/MockVsyncController.h"
 
@@ -42,7 +43,9 @@
                       std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
                       ISchedulerCallback& callback)
           : Scheduler(*this, callback, Feature::kContentDetection) {
-        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
+        mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker),
+                                             std::make_unique<mock::VSyncDispatch>(),
+                                             std::move(controller)));
 
         const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
         registerDisplay(displayId, std::move(selectorPtr));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 113c518..584d52c 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -231,6 +231,8 @@
                 std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
                                                               hal::PowerMode::OFF);
 
+        mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+
         using Callback = scheduler::ISchedulerCallback;
         Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp
                 ? static_cast<Callback&>(mNoOpSchedulerCallback)
@@ -248,6 +250,8 @@
                                                           std::move(selectorPtr), callback);
         }
 
+        mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms);
+
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
         resetScheduler(mScheduler);
@@ -911,6 +915,7 @@
     sp<SurfaceFlinger> mFlinger;
     scheduler::mock::SchedulerCallback mSchedulerCallback;
     scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback;
+    std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
     scheduler::TestableScheduler* mScheduler = nullptr;
 };
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 2b86e94..14a2860 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -270,6 +270,43 @@
     EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
 }
 
+TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) {
+    auto intended = mPeriod - 230;
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+    auto result = mDispatch.schedule(cb,
+                                     {.workDuration = 100,
+                                      .readyDuration = 0,
+                                      .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(900, *result);
+
+    result = mDispatch.update(cb,
+                              {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+    EXPECT_TRUE(result.has_value());
+    EXPECT_EQ(700, *result);
+
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+    EXPECT_THAT(cb.mWakeupTime[0], Eq(700));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmAt(_, _)).Times(0);
+
+    CountingCallback cb(mDispatch);
+    const auto result =
+            mDispatch.update(cb,
+                             {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended});
+    EXPECT_FALSE(result.has_value());
+}
+
 TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
     EXPECT_CALL(mMockClock, alarmAt(_, 1050));
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 8bd689a..1fb2709 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -69,14 +69,6 @@
     std::shared_ptr<Clock> const mClock;
 };
 
-struct MockVSyncDispatch : VSyncDispatch {
-    MOCK_METHOD(CallbackToken, registerCallback, (Callback, std::string), (override));
-    MOCK_METHOD(void, unregisterCallback, (CallbackToken), (override));
-    MOCK_METHOD(ScheduleResult, schedule, (CallbackToken, ScheduleTiming), (override));
-    MOCK_METHOD(CancelResult, cancel, (CallbackToken), (override));
-    MOCK_METHOD(void, dump, (std::string&), (const, override));
-};
-
 std::shared_ptr<android::FenceTime> generateInvalidFence() {
     sp<Fence> fence = sp<Fence>::make();
     return std::make_shared<android::FenceTime>(fence);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp
new file mode 100644
index 0000000..2817514
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/MockVSyncDispatch.h"
+
+namespace android::mock {
+
+// Explicit default instantiation is recommended.
+VSyncDispatch::VSyncDispatch() = default;
+VSyncDispatch::~VSyncDispatch() = default;
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
new file mode 100644
index 0000000..dc32ff9
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncDispatch.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VSyncDispatch.h"
+
+namespace android::mock {
+
+class VSyncDispatch : public android::scheduler::VSyncDispatch {
+public:
+    VSyncDispatch();
+    ~VSyncDispatch() override;
+
+    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(scheduler::CancelResult, cancel, (CallbackToken token), (override));
+    MOCK_METHOD(void, dump, (std::string&), (const, override));
+};
+
+} // namespace android::mock