Merge "Add a workaround to prevent duplicate layers in traces" into main
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 9b1b64a..3efd84f 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1168,7 +1168,8 @@
     socklen_t len = sizeof(serverAddr);
     ret = getsockname(serverFd.get(), reinterpret_cast<sockaddr*>(&serverAddr), &len);
     LOG_ALWAYS_FATAL_IF(0 != ret, "Failed to getsockname: %s", strerror(errno));
-    LOG_ALWAYS_FATAL_IF(len < sizeof(serverAddr), "getsockname didn't read the full addr struct");
+    LOG_ALWAYS_FATAL_IF(len < static_cast<socklen_t>(sizeof(serverAddr)),
+                        "getsockname didn't read the full addr struct");
 
     ret = TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/));
     LOG_ALWAYS_FATAL_IF(0 != ret, "Could not listen socket on port %u: %s", serverAddr.svm_port,
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index f339d41..4424a04 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -260,10 +260,6 @@
 
 void DisplayColorProfile::populateColorModes(
         const DisplayColorProfileCreationArgs::HwcColorModes& hwcColorModes) {
-    if (!hasWideColorGamut()) {
-        return;
-    }
-
     // collect all known SDR render intents
     std::unordered_set<RenderIntent> sdrRenderIntents(sSdrRenderIntents.begin(),
                                                       sSdrRenderIntents.end());
@@ -352,13 +348,9 @@
         *outMode = iter->second.colorMode;
         *outIntent = iter->second.renderIntent;
     } else {
-        // this is unexpected on a WCG display
-        if (hasWideColorGamut()) {
-            ALOGE("map unknown (%s)/(%s) to default color mode",
-                  dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
-                  decodeRenderIntent(intent).c_str());
-        }
-
+        ALOGI("map unknown (%s)/(%s) to default color mode",
+              dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(),
+              decodeRenderIntent(intent).c_str());
         *outDataspace = Dataspace::UNKNOWN;
         *outMode = ColorMode::NATIVE;
         *outIntent = RenderIntent::COLORIMETRIC;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 03a97dc..c354e4a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -123,10 +123,10 @@
                 .build();
     }
 
-    static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport() {
+    static impl::DisplayColorProfile createProfileWithSRGBColorModeSupport(bool wcg = true) {
         return ProfileFactory()
-                .setHasWideColorGamut(true)
                 .addHdrType(Hdr::HDR10)
+                .setHasWideColorGamut(wcg)
                 .addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::COLORIMETRIC)
                 .addColorModeRenderIntent(ColorMode::SRGB, RenderIntent::ENHANCE)
                 .addColorModeRenderIntent(ColorMode::SRGB, VendorRenderIntent)
@@ -289,7 +289,7 @@
 TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasNoSupport) {
     auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
 
-    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
     EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::ENHANCE));
     EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
     EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
@@ -306,6 +306,16 @@
     EXPECT_FALSE(profile.hasRenderIntent(VendorRenderIntent));
 }
 
+TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport_NoWCG) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport(false);
+
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::COLORIMETRIC));
+    EXPECT_TRUE(profile.hasRenderIntent(RenderIntent::ENHANCE));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_COLORIMETRIC));
+    EXPECT_FALSE(profile.hasRenderIntent(RenderIntent::TONE_MAP_ENHANCE));
+    EXPECT_TRUE(profile.hasRenderIntent(VendorRenderIntent));
+}
+
 TEST_F(DisplayColorProfileTest, hasRenderIntentReturnsExpectedValueWhenOutputHasSRGBSupport) {
     auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
 
@@ -476,6 +486,40 @@
     checkGetBestColorMode(profile, expectedResults);
 }
 
+TEST_F(DisplayColorProfileTest,
+       getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport_NoWCG) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport(false);
+
+    // Note: This table of expected values goes with the table of arguments
+    // used in checkGetBestColorMode.
+    using Result = std::tuple<Dataspace, ColorMode, RenderIntent>;
+    std::array<Result, 15> expectedResults = {
+            /* clang-format off */
+            /*  0 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  1 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+            /*  2 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  3 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  4 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+            /*  5 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  6 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /*  7 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::ENHANCE},
+            /*  8 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, VendorRenderIntent},
+
+            /*  9 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 10 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 11 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+
+            /* 12 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 13 */ Result{Dataspace::V0_SRGB, ColorMode::SRGB, RenderIntent::COLORIMETRIC},
+            /* 14 */ Result{Dataspace::UNKNOWN, ColorMode::NATIVE, RenderIntent::COLORIMETRIC},
+            /* clang-format on */
+    };
+
+    checkGetBestColorMode(profile, expectedResults);
+}
+
 TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHasSRGBSupport) {
     auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 8288b99..75b07a8 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -468,6 +468,12 @@
     return false;
 }
 
+void DisplayDevice::onVrrIdle(bool idle) {
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->onVrrIdle(idle);
+    }
+}
+
 void DisplayDevice::animateOverlay() {
     if (mRefreshRateOverlay) {
         mRefreshRateOverlay->animate();
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3cc8cf5..1b8a3a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -196,6 +196,7 @@
     bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
     void animateOverlay();
     bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
+    void onVrrIdle(bool idle);
 
     // Enables an overlay to be display with the hdr/sdr ratio
     void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9527a99..35f12a0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -28,10 +28,11 @@
 
 namespace android {
 
-auto RefreshRateOverlay::draw(int refreshRate, int renderFps, SkColor color,
+auto RefreshRateOverlay::draw(int refreshRate, int renderFps, bool idle, SkColor color,
                               ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
         -> Buffers {
     const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
+    const bool isSetByHwc = features.test(Features::SetByHwc);
 
     Buffers buffers;
     buffers.reserve(loopCount);
@@ -71,7 +72,11 @@
         canvas->setMatrix(canvasTransform);
 
         int left = 0;
-        drawNumber(refreshRate, left, color, *canvas);
+        if (idle && !isSetByHwc) {
+            drawDash(left, *canvas);
+        } else {
+            drawNumber(refreshRate, left, color, *canvas);
+        }
         left += 3 * (kDigitWidth + kDigitSpace);
         if (features.test(Features::Spinner)) {
             switch (i) {
@@ -104,7 +109,11 @@
         left += kDigitWidth + kDigitSpace;
 
         if (features.test(Features::RenderRate)) {
-            drawNumber(renderFps, left, color, *canvas);
+            if (idle) {
+                drawDash(left, *canvas);
+            } else {
+                drawNumber(renderFps, left, color, *canvas);
+            }
         }
         left += 3 * (kDigitWidth + kDigitSpace);
 
@@ -138,6 +147,14 @@
     SegmentDrawer::drawDigit(number % 10, left, color, canvas);
 }
 
+void RefreshRateOverlay::drawDash(int left, SkCanvas& canvas) {
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+}
+
 std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
                                                                ftl::Flags<Features> features) {
     std::unique_ptr<RefreshRateOverlay> overlay =
@@ -171,7 +188,8 @@
     return mSurfaceControl != nullptr;
 }
 
-auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps, bool idle)
+        -> const Buffers& {
     static const Buffers kNoBuffers;
     if (!mSurfaceControl) return kNoBuffers;
 
@@ -197,8 +215,8 @@
 
     createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
 
-    BufferCache::const_iterator it =
-            mBufferCache.find({refreshRate.getIntValue(), renderFps.getIntValue(), transformHint});
+    BufferCache::const_iterator it = mBufferCache.find(
+            {refreshRate.getIntValue(), renderFps.getIntValue(), transformHint, idle});
     if (it == mBufferCache.end()) {
         const int maxFps = mFpsRange.max.getIntValue();
 
@@ -222,10 +240,10 @@
 
         const SkColor color = colorBase.toSkColor();
 
-        auto buffers = draw(refreshIntFps, renderIntFps, color, transformHint, mFeatures);
+        auto buffers = draw(refreshIntFps, renderIntFps, idle, color, transformHint, mFeatures);
         it = mBufferCache
-                     .try_emplace({refreshIntFps, renderIntFps, transformHint}, std::move(buffers))
-                     .first;
+                     .try_emplace({refreshIntFps, renderIntFps, transformHint, idle},
+                     std::move(buffers)).first;
     }
 
     return it->second;
@@ -257,7 +275,15 @@
 void RefreshRateOverlay::changeRefreshRate(Fps refreshRate, Fps renderFps) {
     mRefreshRate = refreshRate;
     mRenderFps = renderFps;
-    const auto buffer = getOrCreateBuffers(refreshRate, renderFps)[mFrame];
+    const auto buffer = getOrCreateBuffers(refreshRate, renderFps, mIsVrrIdle)[mFrame];
+    createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+void RefreshRateOverlay::onVrrIdle(bool idle) {
+    mIsVrrIdle = idle;
+    if (!mRefreshRate || !mRenderFps) return;
+
+    const auto buffer = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle)[mFrame];
     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
 }
 
@@ -265,7 +291,7 @@
     if (mFeatures.test(Features::RenderRate) && mRefreshRate &&
         FlagManager::getInstance().misc1()) {
         mRenderFps = renderFps;
-        const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps)[mFrame];
+        const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps, mIsVrrIdle)[mFrame];
         createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
     }
 }
@@ -273,7 +299,7 @@
 void RefreshRateOverlay::animate() {
     if (!mFeatures.test(Features::Spinner) || !mRefreshRate) return;
 
-    const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps);
+    const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle);
     mFrame = (mFrame + 1) % buffers.size();
     const auto buffer = buffers[mFrame];
     createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index b2896f0..d8aa048 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -57,6 +57,7 @@
     void changeRenderRate(Fps);
     void animate();
     bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+    void onVrrIdle(bool idle);
 
     RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
 
@@ -65,11 +66,12 @@
 
     using Buffers = std::vector<sp<GraphicBuffer>>;
 
-    static Buffers draw(int refreshRate, int renderFps, SkColor, ui::Transform::RotationFlags,
-                        ftl::Flags<Features>);
+    static Buffers draw(int refreshRate, int renderFps, bool idle, SkColor,
+                        ui::Transform::RotationFlags, ftl::Flags<Features>);
     static void drawNumber(int number, int left, SkColor, SkCanvas&);
+    static void drawDash(int left, SkCanvas&);
 
-    const Buffers& getOrCreateBuffers(Fps, Fps);
+    const Buffers& getOrCreateBuffers(Fps, Fps, bool);
 
     SurfaceComposerClient::Transaction createTransaction() const;
 
@@ -77,10 +79,11 @@
         int refreshRate;
         int renderFps;
         ui::Transform::RotationFlags flags;
+        bool idle;
 
         bool operator==(Key other) const {
             return refreshRate == other.refreshRate && renderFps == other.renderFps &&
-                    flags == other.flags;
+                    flags == other.flags && idle == other.idle;
         }
     };
 
@@ -89,6 +92,7 @@
 
     std::optional<Fps> mRefreshRate;
     std::optional<Fps> mRenderFps;
+    bool mIsVrrIdle = false;
     size_t mFrame = 0;
 
     const FpsRange mFpsRange; // For color interpolation.
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 43cdb5e..f430526 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -33,6 +33,7 @@
     virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
                                              Fps renderRate) = 0;
     virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+    virtual void vrrDisplayIdle(bool idle) = 0;
 
 protected:
     ~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index a20cf9a..9f6eab2 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -634,11 +634,12 @@
     // If all layers are category NoPreference, use the current config.
     if (noPreferenceLayers + noVoteLayers == layers.size()) {
         ALOGV("All layers NoPreference");
-        const auto ascendingWithPreferred =
-                rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
+        constexpr float kScore = std::numeric_limits<float>::max();
+        FrameRateRanking currentMode;
+        currentMode.emplace_back(ScoredFrameRate{getActiveModeLocked(), kScore});
         SFTRACE_FORMAT_INSTANT("%s (All layers NoPreference)",
-                               to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
-        return {ascendingWithPreferred, kNoSignals};
+                              to_string(currentMode.front().frameRateMode.fps).c_str());
+        return {currentMode, kNoSignals};
     }
 
     const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
@@ -1500,7 +1501,7 @@
             return str;
         };
         ALOGV("%s render rates: %s, isVrrDevice? %d", rangeName, stringifyModes().c_str(),
-              mIsVrrDevice);
+              mIsVrrDevice.load());
 
         return frameRateModes;
     };
@@ -1510,7 +1511,6 @@
 }
 
 bool RefreshRateSelector::isVrrDevice() const {
-    std::lock_guard lock(mLock);
     return mIsVrrDevice;
 }
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 4f491d9..6f9c146 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -383,6 +383,7 @@
 
         Callbacks platform;
         Callbacks kernel;
+        Callbacks vrr;
     };
 
     void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
@@ -501,6 +502,9 @@
     std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
             REQUIRES(mIdleTimerCallbacksMutex) {
         if (!mIdleTimerCallbacks) return {};
+
+        if (mIsVrrDevice) return mIdleTimerCallbacks->vrr;
+
         return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
                                                              : mIdleTimerCallbacks->platform;
     }
@@ -536,7 +540,7 @@
     std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
 
     // Caches whether the device is VRR-compatible based on the active display mode.
-    bool mIsVrrDevice GUARDED_BY(mLock) = false;
+    std::atomic_bool mIsVrrDevice = false;
 
     Policy mDisplayManagerPolicy GUARDED_BY(mLock);
     std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c43e942..fbd788b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -942,8 +942,9 @@
                 {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
                               .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
                  .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
-                            .onExpired =
-                                    [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+                            .onExpired = [this] { kernelIdleTimerCallback(TimerState::Expired); }},
+                 .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
+                         .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
 
         pacesetter.selectorPtr->startIdleTimer();
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6c1c9c1..605770d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7701,6 +7701,22 @@
     }));
 }
 
+void SurfaceFlinger::vrrDisplayIdle(bool idle) {
+    // Update the overlay on the main thread to avoid race conditions with
+    // RefreshRateSelector::getActiveMode
+    static_cast<void>(mScheduler->schedule([=, this] {
+        const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
+        if (!display) {
+            ALOGW("%s: default display is null", __func__);
+            return;
+        }
+        if (!display->isRefreshRateOverlayEnabled()) return;
+
+        display->onVrrIdle(idle);
+        mScheduler->scheduleFrame();
+    }));
+}
+
 std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
 SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
     const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b128fb7..2787c1f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -704,6 +704,7 @@
                                      Fps renderRate) override;
     void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
             REQUIRES(kMainThreadContext);
+    void vrrDisplayIdle(bool idle) override;
 
     // ICEPowerCallback overrides:
     void notifyCpuLoadUp() override;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index dec5fa5..8f21cdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -31,6 +31,7 @@
     MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
                 (override));
     MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+    MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
 };
 
 struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -41,6 +42,7 @@
     void onChoreographerAttached() override {}
     void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
     void onCommitNotComposited(PhysicalDisplayId) override {}
+    void vrrDisplayIdle(bool) override {}
 };
 
 } // namespace android::scheduler::mock