SurfaceFlinger: use setActiveConfigWithConstraints

Remove setActiveConfig and use setActiveConfigWithConstraints
instead. Use the return parameter of setActiveConfigWithConstraints to know
whether a refresh is required and when.

Fixes: 142753004
Bug: 141329414
Test: observe refresh rate switching thru systrace
Change-Id: Ie67a3be9180e7a367fc9e73425598d53a5fd6578
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d480f7c..d8dad0b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -247,8 +247,8 @@
     // the refresh period and whatever closest timestamp we have.
     std::lock_guard lock(displayData.lastHwVsyncLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
-    return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
+    auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
+    return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
 }
 
 bool HWComposer::isConnected(DisplayId displayId) const {
@@ -291,6 +291,23 @@
     return config;
 }
 
+// Composer 2.4
+
+bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+    return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
+}
+
+nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+    nsecs_t vsyncPeriodNanos;
+    auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+    if (error != HWC2::Error::None) {
+        LOG_DISPLAY_ERROR(displayId, "Failed to get Vsync Period");
+        return 0;
+    }
+
+    return vsyncPeriodNanos;
+}
+
 int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, -1);
 
@@ -541,7 +558,9 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
+status_t HWComposer::setActiveConfigWithConstraints(
+        DisplayId displayId, size_t configId, const HWC2::VsyncPeriodChangeConstraints& constraints,
+        HWC2::VsyncPeriodChangeTimeline* outTimeline) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -550,7 +569,9 @@
         return BAD_INDEX;
     }
 
-    auto error = displayData.hwcDisplay->setActiveConfig(displayData.configMap[configId]);
+    auto error =
+            displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+                                                                   constraints, outTimeline);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e87c5c3..077e452 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -102,9 +102,6 @@
     // set power mode
     virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
 
-    // set active config
-    virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
-
     // Sets a color transform to be applied to the result of composition
     virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
 
@@ -180,6 +177,14 @@
 
     virtual bool isUsingVrComposer() const = 0;
 
+    // Composer 2.4
+    virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+    virtual status_t setActiveConfigWithConstraints(
+            DisplayId displayId, size_t configId,
+            const HWC2::VsyncPeriodChangeConstraints& constraints,
+            HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+
     // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
@@ -232,9 +237,6 @@
     // set power mode
     status_t setPowerMode(DisplayId displayId, int mode) override;
 
-    // set active config
-    status_t setActiveConfig(DisplayId displayId, size_t configId) override;
-
     // Sets a color transform to be applied to the result of composition
     status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
 
@@ -305,6 +307,13 @@
 
     bool isUsingVrComposer() const override;
 
+    // Composer 2.4
+    bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
+    nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
+    status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
+                                            const HWC2::VsyncPeriodChangeConstraints& constraints,
+                                            HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1d50fe1..71a6a2f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -59,11 +59,13 @@
 namespace android {
 
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                     const scheduler::RefreshRateConfigs& refreshRateConfig)
+                     const scheduler::RefreshRateConfigs& refreshRateConfig,
+                     ISchedulerCallback& schedulerCallback)
       : mPrimaryDispSync(new impl::DispSync("SchedulerDispSync",
                                             sysprop::running_without_sync_framework(true))),
         mEventControlThread(new impl::EventControlThread(std::move(function))),
         mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+        mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(refreshRateConfig) {
     using namespace sysprop;
 
@@ -104,10 +106,12 @@
 
 Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
                      std::unique_ptr<EventControlThread> eventControlThread,
-                     const scheduler::RefreshRateConfigs& configs)
+                     const scheduler::RefreshRateConfigs& configs,
+                     ISchedulerCallback& schedulerCallback)
       : mPrimaryDispSync(std::move(primaryDispSync)),
         mEventControlThread(std::move(eventControlThread)),
         mSupportKernelTimer(false),
+        mSchedulerCallback(schedulerCallback),
         mRefreshRateConfigs(configs) {}
 
 Scheduler::~Scheduler() {
@@ -368,12 +372,7 @@
         mFeatures.configId = newConfigId;
     };
     auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
-}
-
-void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mSchedulerCallback = callback;
+    mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
 }
 
 void Scheduler::resetIdleTimer() {
@@ -486,7 +485,7 @@
         }
     }
     const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
-    changeRefreshRate(newRefreshRate, event);
+    mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
 }
 
 HwcConfigIndexType Scheduler::calculateRefreshRateType() {
@@ -526,10 +525,36 @@
     return mFeatures.configId;
 }
 
-void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mSchedulerCallback) {
-        mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
+void Scheduler::onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline) {
+    if (timeline.refreshRequired) {
+        mSchedulerCallback.repaintEverythingForHWC();
+    }
+
+    std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+    mLastVsyncPeriodChangeTimeline = std::make_optional(timeline);
+
+    const auto maxAppliedTime = systemTime() + MAX_VSYNC_APPLIED_TIME.count();
+    if (timeline.newVsyncAppliedTimeNanos > maxAppliedTime) {
+        mLastVsyncPeriodChangeTimeline->newVsyncAppliedTimeNanos = maxAppliedTime;
+    }
+}
+
+void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
+    bool callRepaint = false;
+    {
+        std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+        if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
+            if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
+                mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+            } else {
+                // We need to send another refresh as refreshTimeNanos is still in the future
+                callRepaint = true;
+            }
+        }
+    }
+
+    if (callRepaint) {
+        mSchedulerCallback.repaintEverythingForHWC();
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04a8390..15277ce 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -41,22 +41,24 @@
 class InjectVSyncSource;
 struct DisplayStateInfo;
 
+class ISchedulerCallback {
+public:
+    virtual ~ISchedulerCallback() = default;
+    virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+                                   scheduler::RefreshRateConfigEvent) = 0;
+    virtual void repaintEverythingForHWC() = 0;
+};
+
 class Scheduler {
 public:
     using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
     using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    class ISchedulerCallback {
-    public:
-        virtual ~ISchedulerCallback() = default;
-        virtual void changeRefreshRate(const RefreshRate&, ConfigEvent) = 0;
-    };
-
     // Indicates whether to start the transaction early, or at vsync time.
     enum class TransactionStart { EARLY, NORMAL };
 
     Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
-              const scheduler::RefreshRateConfigs&);
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
 
     virtual ~Scheduler();
 
@@ -114,9 +116,6 @@
     // Detects content using layer history, and selects a matching refresh rate.
     void chooseRefreshRateForContent();
 
-    // Called by Scheduler to control SurfaceFlinger operations.
-    void setSchedulerCallback(ISchedulerCallback*);
-
     bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
@@ -131,6 +130,12 @@
     // Get the appropriate refresh for current conditions.
     std::optional<HwcConfigIndexType> getPreferredConfigId();
 
+    // Notifies the scheduler about a refresh rate timeline change.
+    void onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline);
+
+    // Notifies the scheduler when the display was refreshed
+    void onDisplayRefreshed(nsecs_t timestamp);
+
 private:
     friend class TestableScheduler;
 
@@ -142,7 +147,7 @@
 
     // Used by tests to inject mocks.
     Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
-              const scheduler::RefreshRateConfigs&);
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
 
     std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
                                                            nsecs_t offsetThresholdForNextVsync);
@@ -165,8 +170,6 @@
     void setVsyncPeriod(nsecs_t period);
 
     HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
-    // Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
-    void changeRefreshRate(const RefreshRate&, ConfigEvent);
 
     // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
     struct Connection {
@@ -203,8 +206,7 @@
     // Timer used to monitor display power mode.
     std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
 
-    std::mutex mCallbackLock;
-    ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
+    ISchedulerCallback& mSchedulerCallback;
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
@@ -223,6 +225,11 @@
     } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+
+    std::mutex mVsyncTimelineLock;
+    std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
+            GUARDED_BY(mVsyncTimelineLock);
+    static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5aa2447..71ecf88 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1003,11 +1003,25 @@
     LOG_ALWAYS_FATAL_IF(!displayId);
 
     ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.fps);
-    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId.value());
 
-    // we need to submit an empty frame to HWC to start the process
+    // TODO(b/142753666) use constrains
+    HWC2::VsyncPeriodChangeConstraints constraints;
+    constraints.desiredTimeNanos = systemTime();
+    constraints.seamlessRequired = false;
+
+    HWC2::VsyncPeriodChangeTimeline outTimeline;
+    auto status =
+            getHwComposer().setActiveConfigWithConstraints(*displayId,
+                                                           mUpcomingActiveConfig.configId.value(),
+                                                           constraints, &outTimeline);
+    if (status != NO_ERROR) {
+        LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+        return false;
+    }
+
+    mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+    // Scheduler will submit an empty frame to HWC if needed.
     mCheckPendingFence = true;
-    mEventQueue->invalidate();
     return false;
 }
 
@@ -1360,8 +1374,7 @@
         return 0;
     }
 
-    const auto config = getHwComposer().getActiveConfig(*displayId);
-    return config ? config->getVsyncPeriod() : 0;
+    return getHwComposer().getDisplayVsyncPeriod(*displayId);
 }
 
 void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1447,9 +1460,13 @@
 }
 
 void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
-        int32_t /*sequenceId*/, hwc2_display_t /*display*/,
-        const hwc_vsync_period_change_timeline_t& /*updatedTimeline*/) {
-    // TODO(b/142753004): use timeline when changing refresh rate
+        int32_t sequenceId, hwc2_display_t /*display*/,
+        const hwc_vsync_period_change_timeline_t& updatedTimeline) {
+    Mutex::Autolock lock(mStateLock);
+    if (sequenceId != getBE().mComposerSequenceId) {
+        return;
+    }
+    mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
 }
 
 void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
@@ -1760,11 +1777,17 @@
 
     mGeometryInvalid = false;
 
+    // Store the present time just before calling to the composition engine so we could notify
+    // the scheduler.
+    const auto presentTime = systemTime();
+
     mCompositionEngine->present(refreshArgs);
     mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
     // Reset the frame start time now that we've recorded this frame.
     mFrameStartTime = 0;
 
+    mScheduler->onDisplayRefreshed(presentTime);
+
     postFrame();
     postComposition();
 
@@ -2550,7 +2573,7 @@
     // start the EventThread
     mScheduler =
             getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         *mRefreshRateConfigs);
+                                         *mRefreshRateConfigs, *this);
     mAppConnectionHandle =
             mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
                                          mPhaseOffsets->getOffsetThresholdForNextVsync(),
@@ -2569,8 +2592,6 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
-
-    mScheduler->setSchedulerCallback(this);
 }
 
 void SurfaceFlinger::commitTransaction()
@@ -4306,8 +4327,8 @@
                       "  refresh-rate              : %f fps\n"
                       "  x-dpi                     : %f\n"
                       "  y-dpi                     : %f\n",
-                      1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
-                      activeConfig->getDpiY());
+                      1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
+                      activeConfig->getDpiX(), activeConfig->getDpiY());
     }
 
     StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -5400,6 +5421,27 @@
 void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
                                                       const std::vector<int32_t>& allowedConfigs) {
     if (!display->isPrimary()) {
+        // TODO(b/144711714): For non-primary displays we should be able to set an active config
+        // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+        // it should go thru setDesiredActiveConfig, similar to primary display.
+        ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+        const auto displayId = display->getId();
+        LOG_ALWAYS_FATAL_IF(!displayId);
+
+        HWC2::VsyncPeriodChangeConstraints constraints;
+        constraints.desiredTimeNanos = systemTime();
+        constraints.seamlessRequired = false;
+
+        HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+        getHwComposer().setActiveConfigWithConstraints(*displayId, allowedConfigs[0], constraints,
+                                                       &timeline);
+        if (timeline.refreshRequired) {
+            repaintEverythingForHWC();
+        }
+
+        auto configId = HwcConfigIndexType(allowedConfigs[0]);
+        display->setActiveConfig(configId);
+        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId);
         return;
     }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 900c5f7..b5bc5c5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -175,7 +175,7 @@
                        public ClientCache::ErasedRecipient,
                        private IBinder::DeathRecipient,
                        private HWC2::ComposerCallback,
-                       private Scheduler::ISchedulerCallback {
+                       private ISchedulerCallback {
 public:
     SurfaceFlingerBE& getBE() { return mBE; }
     const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -272,9 +272,6 @@
     // force full composition on all displays
     void repaintEverything();
 
-    // force full composition on all displays without resetting the scheduler idle timer.
-    void repaintEverythingForHWC();
-
     surfaceflinger::Factory& getFactory() { return mFactory; }
 
     // The CompositionEngine encapsulates all composition related interfaces and actions.
@@ -496,9 +493,11 @@
             const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
 
     /* ------------------------------------------------------------------------
-     * Scheduler::ISchedulerCallback
+     * ISchedulerCallback
      */
     void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+    // force full composition on all displays without resetting the scheduler idle timer.
+    void repaintEverythingForHWC() override;
     /* ------------------------------------------------------------------------
      * Message handling
      */
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 4f439da..bd4cdba 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -64,8 +64,9 @@
 }
 
 std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
-        SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs) {
-    return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs);
+        SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
+        ISchedulerCallback& schedulerCallback) {
+    return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback);
 }
 
 std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 42bb177..1a24448 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -32,7 +32,8 @@
     std::unique_ptr<MessageQueue> createMessageQueue() override;
     std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override;
     std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
-                                               const scheduler::RefreshRateConfigs&) override;
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override;
     std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
     sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 20784d2..0db941d 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -40,6 +40,7 @@
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
+class ISchedulerCallback;
 class Layer;
 class MessageQueue;
 class Scheduler;
@@ -75,7 +76,8 @@
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
     virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
     virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
-                                                       const scheduler::RefreshRateConfigs&) = 0;
+                                                       const scheduler::RefreshRateConfigs&,
+                                                       ISchedulerCallback&) = 0;
     virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 40c00c4..fa095aa 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -26,17 +26,17 @@
 
 namespace android {
 
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ISchedulerCallback {
 public:
     explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
-          : Scheduler([](bool) {}, configs) {
+          : Scheduler([](bool) {}, configs, *this) {
         mLayerHistory.emplace();
     }
 
     TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
                       std::unique_ptr<EventControlThread> eventControlThread,
                       const scheduler::RefreshRateConfigs& configs)
-          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
         mLayerHistory.emplace();
     }
 
@@ -68,6 +68,10 @@
         mutablePrimaryDispSync().reset();
         mConnections.clear();
     }
+
+private:
+    void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+    void repaintEverythingForHWC() override {}
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c4b8408..8a7c132 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -84,7 +84,8 @@
     }
 
     std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
-                                               const scheduler::RefreshRateConfigs&) override {
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override {
         return nullptr;
     }