Merge changes I101f4015,Ia236fd4a,I8a00657e into sc-dev

* changes:
  SF: Move mode caching from Scheduler to RefreshRateConfigs
  SF: Always create LayerHistory in Scheduler
  SF: Recreate display on resolution changes
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b062acd..9746076 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -190,6 +190,45 @@
 RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
                                                    const GlobalSignals& globalSignals,
                                                    GlobalSignals* outSignalsConsidered) const {
+    std::lock_guard lock(mLock);
+
+    if (auto cached = getCachedBestRefreshRate(layers, globalSignals, outSignalsConsidered)) {
+        return *cached;
+    }
+
+    GlobalSignals signalsConsidered;
+    RefreshRate result = getBestRefreshRateLocked(layers, globalSignals, &signalsConsidered);
+    lastBestRefreshRateInvocation.emplace(
+            GetBestRefreshRateInvocation{.layerRequirements = layers,
+                                         .globalSignals = globalSignals,
+                                         .outSignalsConsidered = signalsConsidered,
+                                         .resultingBestRefreshRate = result});
+    if (outSignalsConsidered) {
+        *outSignalsConsidered = signalsConsidered;
+    }
+    return result;
+}
+
+std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
+    const bool sameAsLastCall = lastBestRefreshRateInvocation &&
+            lastBestRefreshRateInvocation->layerRequirements == layers &&
+            lastBestRefreshRateInvocation->globalSignals == globalSignals;
+
+    if (sameAsLastCall) {
+        if (outSignalsConsidered) {
+            *outSignalsConsidered = lastBestRefreshRateInvocation->outSignalsConsidered;
+        }
+        return lastBestRefreshRateInvocation->resultingBestRefreshRate;
+    }
+
+    return {};
+}
+
+RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
     ATRACE_CALL();
     ALOGV("getBestRefreshRate %zu layers", layers.size());
 
@@ -206,8 +245,6 @@
         }
     };
 
-    std::lock_guard lock(mLock);
-
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -592,6 +629,11 @@
 
 void RefreshRateConfigs::setCurrentModeId(DisplayModeId modeId) {
     std::lock_guard lock(mLock);
+
+    // Invalidate the cached invocation to getBestRefreshRate. This forces
+    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+    lastBestRefreshRateInvocation.reset();
+
     mCurrentRefreshRate = mRefreshRates.at(modeId).get();
 }
 
@@ -605,11 +647,16 @@
 void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
                                             DisplayModeId currentModeId) {
     std::lock_guard lock(mLock);
+
     // The current mode should be supported
     LOG_ALWAYS_FATAL_IF(std::none_of(modes.begin(), modes.end(), [&](DisplayModePtr mode) {
         return mode->getId() == currentModeId;
     }));
 
+    // Invalidate the cached invocation to getBestRefreshRate. This forces
+    // the refresh rate to be recomputed on the next call to getBestRefreshRate.
+    lastBestRefreshRateInvocation.reset();
+
     mRefreshRates.clear();
     for (const auto& mode : modes) {
         const auto modeId = mode->getId();
@@ -666,6 +713,7 @@
         ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
         return BAD_VALUE;
     }
+    lastBestRefreshRateInvocation.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mDisplayManagerPolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
@@ -680,6 +728,7 @@
     if (policy && !isPolicyValidLocked(*policy)) {
         return BAD_VALUE;
     }
+    lastBestRefreshRateInvocation.reset();
     Policy previousPolicy = *getCurrentPolicyLocked();
     mOverridePolicy = policy;
     if (*getCurrentPolicyLocked() == previousPolicy) {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ee89149..342fde0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -250,6 +250,10 @@
         bool touch = false;
         // True if the system hasn't seen any buffers posted to layers recently.
         bool idle = false;
+
+        bool operator==(const GlobalSignals& other) const {
+            return touch == other.touch && idle == other.idle;
+        }
     };
 
     // Returns the refresh rate that fits best to the given layers.
@@ -350,6 +354,15 @@
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
             std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
 
+    std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                                        const GlobalSignals& globalSignals,
+                                                        GlobalSignals* outSignalsConsidered) const
+            REQUIRES(mLock);
+
+    RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
+                                         const GlobalSignals& globalSignals,
+                                         GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
+
     // Returns the refresh rate with the highest score in the collection specified from begin
     // to end. If there are more than one with the same highest refresh rate, the first one is
     // returned.
@@ -414,6 +427,15 @@
 
     const bool mEnableFrameRateOverride;
     bool mSupportsFrameRateOverride;
+
+    struct GetBestRefreshRateInvocation {
+        std::vector<LayerRequirement> layerRequirements;
+        GlobalSignals globalSignals;
+        GlobalSignals outSignalsConsidered;
+        RefreshRate resultingBestRefreshRate;
+    };
+    mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
+            GUARDED_BY(mLock);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 80f4665..208a767 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -61,6 +61,7 @@
         if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
             return;
         }
+        mTimeStats.incrementRefreshRateSwitches();
         flushTime();
         mCurrentRefreshRate = currRefreshRate;
     }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index fac2c65..4b8cbfb 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -194,8 +194,6 @@
 
 std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
         const scheduler::RefreshRateConfigs& configs) {
-    if (!configs.canSwitch()) return nullptr;
-
     return std::make_unique<scheduler::LayerHistory>(configs);
 }
 
@@ -579,8 +577,6 @@
 }
 
 void Scheduler::registerLayer(Layer* layer) {
-    if (!mLayerHistory) return;
-
     scheduler::LayerHistory::LayerVoteType voteType;
 
     if (!mOptions.useContentDetection ||
@@ -600,26 +596,22 @@
 }
 
 void Scheduler::deregisterLayer(Layer* layer) {
-    if (mLayerHistory) {
-        mLayerHistory->deregisterLayer(layer);
-    }
+    mLayerHistory->deregisterLayer(layer);
 }
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
                                    LayerHistory::LayerUpdateType updateType) {
-    if (mLayerHistory) {
+    if (mRefreshRateConfigs.canSwitch()) {
         mLayerHistory->record(layer, presentTime, systemTime(), updateType);
     }
 }
 
 void Scheduler::setModeChangePending(bool pending) {
-    if (mLayerHistory) {
-        mLayerHistory->setModeChangePending(pending);
-    }
+    mLayerHistory->setModeChangePending(pending);
 }
 
 void Scheduler::chooseRefreshRateForContent() {
-    if (!mLayerHistory) return;
+    if (!mRefreshRateConfigs.canSwitch()) return;
 
     ATRACE_CALL();
 
@@ -630,9 +622,6 @@
     bool frameRateOverridesChanged;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mFeatures.contentRequirements == summary) {
-            return;
-        }
         mFeatures.contentRequirements = summary;
 
         newModeId = calculateRefreshRateModeId(&consideredSignals);
@@ -691,9 +680,7 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    if (mLayerHistory) {
-        mLayerHistory->clear();
-    }
+    mLayerHistory->clear();
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -732,9 +719,7 @@
     // NOTE: Instead of checking all the layers, we should be checking the layer
     // that is currently on top. b/142507166 will give us this capability.
     if (handleTimerStateChanged(&mFeatures.touch, touch)) {
-        if (mLayerHistory) {
-            mLayerHistory->clear();
-        }
+        mLayerHistory->clear();
     }
     ATRACE_INT("TouchState", static_cast<int>(touch));
 }
@@ -908,9 +893,7 @@
 }
 
 void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
-    if (mLayerHistory) {
-        mLayerHistory->setDisplayArea(displayArea);
-    }
+    mLayerHistory->setDisplayArea(displayArea);
 }
 
 void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 49d3d93..30a3253 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -268,7 +268,7 @@
     VsyncSchedule mVsyncSchedule;
 
     // Used to choose refresh rate if content detection is enabled.
-    const std::unique_ptr<LayerHistory> mLayerHistory;
+    std::unique_ptr<LayerHistory> mLayerHistory;
 
     // Timer that records time between requests for next vsync.
     std::optional<scheduler::OneShotTimer> mIdleTimer;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 59bc28f..f02f1e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1151,7 +1151,18 @@
         // have been already updated with the upcoming active mode.
         return;
     }
-    const Fps oldRefreshRate = display->getActiveMode()->getFps();
+
+    if (display->getActiveMode()->getSize() != upcomingMode->getSize()) {
+        auto& state = mCurrentState.displays.editValueFor(display->getDisplayToken());
+        // We need to generate new sequenceId in order to recreate the display (and this
+        // way the framebuffer).
+        state.sequenceId = DisplayDeviceState{}.sequenceId;
+        state.physical->activeMode = upcomingMode;
+        processDisplayChangesLocked();
+
+        // processDisplayChangesLocked will update all necessary components so we're done here.
+        return;
+    }
 
     std::lock_guard<std::mutex> lock(mActiveModeLock);
     mRefreshRateConfigs->setCurrentModeId(mUpcomingActiveMode.modeId);
@@ -1161,9 +1172,6 @@
 
     mRefreshRateStats->setRefreshRate(refreshRate);
 
-    if (!refreshRate.equalsWithMargin(oldRefreshRate)) {
-        mTimeStats->incrementRefreshRateSwitches();
-    }
     updatePhaseConfiguration(refreshRate);
     ATRACE_INT("ActiveConfigFPS", refreshRate.getValue());
 
@@ -2821,7 +2829,10 @@
                 mRefreshRateConfigs->updateDisplayModes(currentState.physical->supportedModes,
                                                         currentState.physical->activeMode->getId());
                 mVsyncConfiguration->reset();
-                updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate().getFps());
+                const Fps refreshRate = currentState.physical->activeMode->getFps();
+                updatePhaseConfiguration(refreshRate);
+                mRefreshRateStats->setRefreshRate(refreshRate);
+
                 if (mRefreshRateOverlay) {
                     mRefreshRateOverlay->reset();
                 }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7ace70a..d04a7d7 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -45,9 +45,16 @@
 
 class RefreshRateConfigsTest : public testing::Test {
 protected:
+    using GetBestRefreshRateInvocation = RefreshRateConfigs::GetBestRefreshRateInvocation;
+
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
+    RefreshRate createRefreshRate(DisplayModePtr displayMode) {
+        return {displayMode->getId(), displayMode, displayMode->getFps(),
+                RefreshRate::ConstructorTag(0)};
+    }
+
     Fps findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, Fps frameRate) {
         return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
     }
@@ -71,6 +78,19 @@
         return *refreshRateConfigs.mMaxSupportedRefreshRate;
     }
 
+    void setLastBestRefreshRateInvocation(RefreshRateConfigs& refreshRateConfigs,
+                                          const GetBestRefreshRateInvocation& invocation) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        refreshRateConfigs.lastBestRefreshRateInvocation.emplace(
+                GetBestRefreshRateInvocation(invocation));
+    }
+
+    std::optional<GetBestRefreshRateInvocation> getLastBestRefreshRateInvocation(
+            const RefreshRateConfigs& refreshRateConfigs) {
+        std::lock_guard lock(refreshRateConfigs.mLock);
+        return refreshRateConfigs.lastBestRefreshRateInvocation;
+    }
+
     // Test config IDs
     static inline const DisplayModeId HWC_CONFIG_ID_60 = DisplayModeId(0);
     static inline const DisplayModeId HWC_CONFIG_ID_90 = DisplayModeId(1);
@@ -1752,6 +1772,78 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
+    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    setLastBestRefreshRateInvocation(*refreshRateConfigs,
+                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+                                                                          LayerRequirement>(),
+                                                                  .globalSignals = {.touch = true,
+                                                                                    .idle = true},
+                                                                  .outSignalsConsidered =
+                                                                          {.touch = true,
+                                                                           .idle = false},
+                                                                  .resultingBestRefreshRate =
+                                                                          createRefreshRate(
+                                                                                  mConfig90)});
+
+    EXPECT_EQ(createRefreshRate(mConfig90),
+              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+                                                     {.touch = true, .idle = true}));
+
+    const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+    setLastBestRefreshRateInvocation(*refreshRateConfigs,
+                                     GetBestRefreshRateInvocation{.layerRequirements = std::vector<
+                                                                          LayerRequirement>(),
+                                                                  .globalSignals = {.touch = true,
+                                                                                    .idle = true},
+                                                                  .outSignalsConsidered =
+                                                                          cachedSignalsConsidered,
+                                                                  .resultingBestRefreshRate =
+                                                                          createRefreshRate(
+                                                                                  mConfig30)});
+
+    GlobalSignals signalsConsidered;
+    EXPECT_EQ(createRefreshRate(mConfig30),
+              refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
+                                                     {.touch = true, .idle = true},
+                                                     &signalsConsidered));
+
+    EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_WritesCache) {
+    using GlobalSignals = RefreshRateConfigs::GlobalSignals;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
+
+    GlobalSignals globalSignals{.touch = true, .idle = true};
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 0.5f}};
+    const auto lastResult =
+            refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
+                                                   /* outSignalsConsidered */ nullptr);
+
+    const auto lastInvocation = getLastBestRefreshRateInvocation(*refreshRateConfigs);
+
+    ASSERT_TRUE(lastInvocation.has_value());
+    ASSERT_EQ(layers, lastInvocation->layerRequirements);
+    ASSERT_EQ(globalSignals, lastInvocation->globalSignals);
+    ASSERT_EQ(lastResult, lastInvocation->resultingBestRefreshRate);
+
+    // outSignalsConsidered needs to be populated even tho earlier we gave nullptr
+    // to getBestRefreshRate()
+    GlobalSignals detaultSignals;
+    ASSERT_FALSE(detaultSignals == lastInvocation->outSignalsConsidered);
+}
+
 TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
     EXPECT_TRUE(mExpected60Config < mExpected90Config);
     EXPECT_FALSE(mExpected60Config < mExpected60Config);
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 38e503f..423d0cc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -51,9 +51,18 @@
 
     SchedulerTest();
 
-    const scheduler::RefreshRateConfigs
-            mConfigs{{DisplayMode::Builder(0).setVsyncPeriod(16'666'667).setGroup(0).build()},
-                     DisplayModeId(0)};
+    const DisplayModePtr mode60 = DisplayMode::Builder(0)
+                                          .setId(DisplayModeId(0))
+                                          .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+                                          .setGroup(0)
+                                          .build();
+    const DisplayModePtr mode120 = DisplayMode::Builder(1)
+                                           .setId(DisplayModeId(1))
+                                           .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+                                           .setGroup(0)
+                                           .build();
+
+    scheduler::RefreshRateConfigs mConfigs{{mode60}, mode60->getId()};
 
     mock::SchedulerCallback mSchedulerCallback;
 
@@ -149,15 +158,14 @@
     EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
-TEST_F(SchedulerTest, noLayerHistory) {
-    // Layer history should not be created if there is a single config.
-    ASSERT_FALSE(mScheduler->hasLayerHistory());
-
+TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
+    // The layer is registered at creation time and deregistered at destruction time.
     sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
 
-    // Content detection should be no-op.
-    mScheduler->registerLayer(layer.get());
+    // recordLayerHistory should be a noop
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
     mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
 
     constexpr bool kPowerStateNormal = true;
     mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -169,6 +177,18 @@
     mScheduler->chooseRefreshRateForContent();
 }
 
+TEST_F(SchedulerTest, updateDisplayModes) {
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
+    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+    ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+
+    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+    ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+    ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+}
+
 TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
     // If the optional fields are cleared, the function should return before
     // onModeChange is called.
@@ -200,4 +220,25 @@
     EXPECT_EQ(0, mFlinger.calculateExtraBufferCount(Fps(60), 10ms));
 }
 
+MATCHER(Is120Hz, "") {
+    return arg.getFps().equalsWithMargin(Fps(120.f));
+}
+
+TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
+    mConfigs.updateDisplayModes({mode60, mode120}, /* activeMode */ mode60->getId());
+
+    sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+
+    mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
+
+    constexpr bool kPowerStateNormal = true;
+    mScheduler->setDisplayPowerState(kPowerStateNormal);
+
+    constexpr uint32_t kDisplayArea = 999'999;
+    mScheduler->onPrimaryDisplayAreaChanged(kDisplayArea);
+
+    EXPECT_CALL(mSchedulerCallback, changeRefreshRate(Is120Hz(), _)).Times(1);
+    mScheduler->chooseRefreshRateForContent();
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 3f9dd01..41fd6e3 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -64,6 +64,11 @@
         return mutableLayerHistory()->mLayerInfos.size();
     }
 
+    size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
+        if (!mLayerHistory) return 0;
+        return mutableLayerHistory()->mActiveLayersEnd;
+    }
+
     void replaceTouchTimer(int64_t millis) {
         if (mTouchTimer) {
             mTouchTimer.reset();