Merge "Fix latching of final buffer when moving off-screen and destroying" into sc-dev
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 364c939..e15e11c 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -151,7 +151,7 @@
                                                       1, false);
     static int32_t id = 0;
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
-    mPendingBufferTrace = "PendingBuffer - " + mName + "BLAST#" + std::to_string(id);
+    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(id);
     id++;
     mBufferItemConsumer->setName(String8(consumerName.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
@@ -361,16 +361,15 @@
                      graphicBufferId);
             return;
         }
-
+        mNumAcquired--;
         mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
         mSubmitted.erase(it);
+        processNextBufferLocked(false /* useNextTransaction */);
     }
 
     ATRACE_INT("PendingRelease", mPendingRelease.size());
-
-    mNumAcquired--;
-    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
-    processNextBufferLocked(false /* useNextTransaction */);
+    ATRACE_INT(mQueuedBufferTrace.c_str(),
+               mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
     mCallbackCV.notify_all();
 }
 
@@ -538,7 +537,8 @@
     }
     // add to shadow queue
     mNumFrameAvailable++;
-    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
+    ATRACE_INT(mQueuedBufferTrace.c_str(),
+               mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
 
     BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
              toString(nextTransactionSet));
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index c69435d..fd78309 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -182,12 +182,12 @@
 
 void TransactionCompletedListener::addJankListener(const sp<JankDataListener>& listener,
                                                    sp<SurfaceControl> surfaceControl) {
-    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     mJankListeners.insert({surfaceControl->getHandle(), listener});
 }
 
 void TransactionCompletedListener::removeJankListener(const sp<JankDataListener>& listener) {
-    std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
+    std::lock_guard<std::mutex> lock(mMutex);
     for (auto it = mJankListeners.begin(); it != mJankListeners.end();) {
         if (it->second == listener) {
             it = mJankListeners.erase(it);
@@ -242,6 +242,7 @@
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
+    std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap;
     {
         std::lock_guard<std::mutex> lock(mMutex);
 
@@ -257,6 +258,7 @@
          * sp<SurfaceControl> that could possibly exist for the callbacks.
          */
         callbacksMap = mCallbacks;
+        jankListenersMap = mJankListeners;
         for (const auto& transactionStats : listenerStats.transactionStats) {
             for (auto& callbackId : transactionStats.callbackIds) {
                 mCallbacks.erase(callbackId);
@@ -352,12 +354,7 @@
             }
 
             if (surfaceStats.jankData.empty()) continue;
-
-            // Acquire jank listener lock such that we guarantee that after calling unregister,
-            // there won't be any further callback.
-            std::scoped_lock<std::recursive_mutex> lock(mJankListenerMutex);
-            auto copy = mJankListeners;
-            auto jankRange = copy.equal_range(surfaceStats.surfaceControl);
+            auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl);
             for (auto it = jankRange.first; it != jankRange.second; it++) {
                 it->second->onJankDataAvailable(surfaceStats.jankData);
             }
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index cb0e65e..26c7285 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -125,7 +125,11 @@
     static PixelFormat convertBufferFormat(PixelFormat& format);
 
     std::string mName;
-    std::string mPendingBufferTrace;
+    // Represents the queued buffer count from buffer queue,
+    // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) +
+    // mNumAcquired (buffers that queued to SF)  mPendingRelease.size() (buffers that are held by
+    // blast). This counter is read by android studio profiler.
+    std::string mQueuedBufferTrace;
     sp<SurfaceControl> mSurfaceControl;
 
     std::mutex mMutex;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index fa91bfa..13994fd 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -653,9 +653,6 @@
     std::mutex mMutex;
 
     // This lock needs to be recursive so we can unregister a callback from within that callback.
-    std::recursive_mutex mJankListenerMutex;
-
-    // This lock needs to be recursive so we can unregister a callback from within that callback.
     std::recursive_mutex mSurfaceStatsListenerMutex;
 
     bool mListening GUARDED_BY(mMutex) = false;
@@ -680,10 +677,7 @@
 
     std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> mCallbacks
             GUARDED_BY(mMutex);
-
-    // This is protected by mJankListenerMutex, but GUARDED_BY isn't supported for
-    // std::recursive_mutex
-    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners;
+    std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex);
     std::unordered_map<uint64_t /* graphicsBufferId */, ReleaseBufferCallback>
             mReleaseBufferCallbacks GUARDED_BY(mMutex);
 
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index b3975b0..725f57b 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -72,6 +72,8 @@
             // have color correction added, and important that it be srgb, so the *vertex* shader
             // doesn't have color correction added.
             .sourceDataspace = kDestDataSpace,
+            // setting this is mandatory for shadows and blurs
+            .skipContentDraw = true,
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
@@ -173,9 +175,12 @@
                             .boundaries = rect,
                     },
             .alpha = 1,
+            // setting this is mandatory for shadows and blurs
+            .skipContentDraw = true,
     };
 
     auto layers = std::vector<const LayerSettings*>{&layer};
+    // Different blur code is invoked for radii less and greater than 30 pixels
     for (int radius : {9, 60}) {
         layer.backgroundBlurRadius = radius;
         renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
@@ -290,10 +295,13 @@
                                                   ExternalTexture::Usage::READABLE |
                                                           ExternalTexture::Usage::WRITEABLE);
 
+        // 6 shaders
         drawSolidLayers(renderengine, display, dstTexture);
+        // 8 shaders
         drawShadowLayers(renderengine, display, srcTexture);
 
         if (renderengine->supportsBackgroundBlur()) {
+            // 2 shaders
             drawBlurLayers(renderengine, display, dstTexture);
         }
 
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2281721..726fe8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2161,8 +2161,9 @@
 
 bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) {
     int targetSdk = getTargetSdkVersion(opPackageName);
-    bool hasSamplingRatePermission = PermissionCache::checkCallingPermission(
-                    sAccessHighSensorSamplingRatePermission);
+    bool hasSamplingRatePermission = checkPermission(sAccessHighSensorSamplingRatePermission,
+            IPCThreadState::self()->getCallingPid(),
+            IPCThreadState::self()->getCallingUid());
     if (targetSdk < __ANDROID_API_S__ ||
             (targetSdk >= __ANDROID_API_S__ && hasSamplingRatePermission)) {
         return false;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index acc7ed2..22d9dc6 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -378,10 +378,14 @@
     if (mLayers.size() == 1) {
         base::StringAppendF(&result, "    Layer [%s]\n", mLayers[0].getName().c_str());
         base::StringAppendF(&result, "    Buffer %p", mLayers[0].getBuffer().get());
+        base::StringAppendF(&result, "    Protected [%s]",
+                            mLayers[0].getState()->isProtected() ? "true" : "false");
     } else {
         result.append("    Cached set of:");
         for (const Layer& layer : mLayers) {
             base::StringAppendF(&result, "\n      Layer [%s]", layer.getName().c_str());
+            base::StringAppendF(&result, "\n      Protected [%s]",
+                                layer.getState()->isProtected() ? "true" : "false");
         }
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2bcaf60..153cee3 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -420,7 +420,7 @@
         const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
-            !currentSet->hasHdrLayers() && !currentSet->hasProtectedLayers()) {
+            !currentSet->hasHdrLayers()) {
             if (isPartOfRun) {
                 builder.append(currentSet->getLayerCount());
             } else {
@@ -491,6 +491,14 @@
         return;
     }
 
+    for (const CachedSet& layer : mLayers) {
+        // TODO (b/191997217): make it less aggressive, and sync with findCandidateRuns
+        if (layer.hasProtectedLayers()) {
+            ATRACE_NAME("layer->hasProtectedLayers()");
+            return;
+        }
+    }
+
     std::vector<Run> runs = findCandidateRuns(now);
 
     std::optional<Run> bestRun = findBestRun(runs);
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0eb16e2..0334d70 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -107,9 +107,39 @@
     return {quotient, remainder};
 }
 
+bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
+                                       const RefreshRate& refreshRate) const {
+    switch (layer.vote) {
+        case LayerVoteType::ExplicitExactOrMultiple:
+        case LayerVoteType::Heuristic:
+            if (mConfig.frameRateMultipleThreshold != 0 &&
+                refreshRate.fps.greaterThanOrEqualWithMargin(
+                        Fps(mConfig.frameRateMultipleThreshold)) &&
+                layer.desiredRefreshRate.lessThanWithMargin(
+                        Fps(mConfig.frameRateMultipleThreshold / 2))) {
+                // Don't vote high refresh rates past the threshold for layers with a low desired
+                // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
+                // 120 Hz, but desired 60 fps should have a vote.
+                return false;
+            }
+            break;
+        case LayerVoteType::ExplicitDefault:
+        case LayerVoteType::ExplicitExact:
+        case LayerVoteType::Max:
+        case LayerVoteType::Min:
+        case LayerVoteType::NoVote:
+            break;
+    }
+    return true;
+}
+
 float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
                                                     const RefreshRate& refreshRate,
                                                     bool isSeamlessSwitch) const {
+    if (!isVoteAllowed(layer, refreshRate)) {
+        return 0;
+    }
+
     // Slightly prefer seamless switches.
     constexpr float kSeamedSwitchPenalty = 0.95f;
     const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
@@ -331,8 +361,9 @@
     const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
 
     for (const auto& layer : layers) {
-        ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
-              layerVoteTypeString(layer.vote).c_str(), layer.weight);
+        ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
+              layerVoteTypeString(layer.vote).c_str(), layer.weight,
+              layer.desiredRefreshRate.getValue());
         if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
             continue;
         }
@@ -646,9 +677,8 @@
 }
 
 RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
-                                       bool enableFrameRateOverride)
-      : mKnownFrameRates(constructKnownFrameRates(modes)),
-        mEnableFrameRateOverride(enableFrameRateOverride) {
+                                       Config config)
+      : mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
     updateDisplayModes(modes, currentModeId);
 }
 
@@ -685,7 +715,7 @@
     mMaxSupportedRefreshRate = sortedModes.back();
 
     mSupportsFrameRateOverride = false;
-    if (mEnableFrameRateOverride) {
+    if (mConfig.enableFrameRateOverride) {
         for (const auto& mode1 : sortedModes) {
             for (const auto& mode2 : sortedModes) {
                 if (getFrameRateDivider(mode1->getFps(), mode2->getFps()) >= 2) {
@@ -859,6 +889,7 @@
     const auto& deviceMin = *mMinSupportedRefreshRate;
     const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
     const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+    const auto& currentPolicy = getCurrentPolicyLocked();
 
     // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
     // the min allowed refresh rate is higher than the device min, we do not want to enable the
@@ -867,6 +898,10 @@
         return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
     }
     if (minByPolicy == maxByPolicy) {
+        // when min primary range in display manager policy is below device min turn on the timer.
+        if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+            return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+        }
         return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
     }
     // Turn on the timer in all other cases.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 6cd0f42..dfd1395 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -53,7 +53,7 @@
 public:
     // Margin used when matching refresh rates to the content desired ones.
     static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
-        std::chrono::nanoseconds(800us).count();
+            std::chrono::nanoseconds(800us).count();
 
     class RefreshRate {
     private:
@@ -302,8 +302,19 @@
     // Returns a known frame rate that is the closest to frameRate
     Fps findClosestKnownFrameRate(Fps frameRate) const;
 
+    // Configuration flags.
+    struct Config {
+        bool enableFrameRateOverride = false;
+
+        // Specifies the upper refresh rate threshold (inclusive) for layer vote types of multiple
+        // or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
+        // no threshold is set.
+        int frameRateMultipleThreshold = 0;
+    };
+
     RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
-                       bool enableFrameRateOverride = false);
+                       Config config = {.enableFrameRateOverride = false,
+                                        .frameRateMultipleThreshold = 0});
 
     void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
 
@@ -315,8 +326,7 @@
         return mRefreshRates.size() > 1;
     }
 
-    // Class to enumerate options around toggling the kernel timer on and off. We have an option
-    // for no change to avoid extra calls to kernel.
+    // Class to enumerate options around toggling the kernel timer on and off.
     enum class KernelIdleTimerAction {
         TurnOff,  // Turn off the idle timer.
         TurnOn    // Turn on the idle timer.
@@ -387,6 +397,9 @@
     const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
     bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock);
 
+    // Returns whether the layer is allowed to vote for the given refresh rate.
+    bool isVoteAllowed(const LayerRequirement&, const RefreshRate&) const;
+
     // calculates a score for a layer. Used to determine the display refresh rate
     // and the frame rate override for certains applications.
     float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
@@ -424,7 +437,7 @@
     // from based on the closest value.
     const std::vector<Fps> mKnownFrameRates;
 
-    const bool mEnableFrameRateOverride;
+    const Config mConfig;
     bool mSupportsFrameRateOverride;
 
     struct GetBestRefreshRateInvocation {
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f346465..e2f3ebb 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3118,10 +3118,15 @@
         return;
     }
     const auto displayId = displayState.physical->id;
-    mRefreshRateConfigs = std::make_unique<
-            scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
-                                           displayState.physical->activeMode->getId(),
-                                           android::sysprop::enable_frame_rate_override(false));
+    scheduler::RefreshRateConfigs::Config config =
+            {.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
+             .frameRateMultipleThreshold =
+                     base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)};
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(displayState.physical->supportedModes,
+                                                            displayState.physical->activeMode
+                                                                    ->getId(),
+                                                            config);
     const auto currRefreshRate = displayState.physical->activeMode->getFps();
     mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate,
                                                                       hal::PowerMode::OFF);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index d4b229f..3423bd5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -165,6 +165,7 @@
                                      RefreshRate::ConstructorTag(0)};
     RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, Fps(120),
                                       RefreshRate::ConstructorTag(0)};
+
 private:
     DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
                                      ui::Size resolution = ui::Size());
@@ -487,6 +488,52 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 90};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(90.0f);
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(60.0f);
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(45.0f);
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(30.0f);
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = Fps(24.0f);
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device,
@@ -649,6 +696,99 @@
               refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(60.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "60Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.name = "24Hz Heuristic";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = Fps(24.0f);
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = Fps(90.0f);
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "90Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -819,6 +959,24 @@
     }
 }
 
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo_multipleThreshold_60_120) {
+    RefreshRateConfigs::Config config = {.frameRateMultipleThreshold = 120};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = Fps(fps);
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+    }
+}
+
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -1732,10 +1890,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                 LayerRequirement{.weight = 0.5f}};
@@ -1846,10 +2004,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactTouchBoost) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_120Device,
-                                                 /*currentConfigId=*/HWC_CONFIG_ID_60,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
                                                 LayerRequirement{.weight = 0.5f}};
@@ -1908,6 +2066,35 @@
     EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
 }
 
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerActionFor120Hz) {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    // Tests with 120Hz
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_120);
+    // SetPolicy(0, 60), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(0), Fps(60)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 60), current 60Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 120), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(120)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(120, 120), current 120Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_120, {Fps(120), Fps(120)}}),
+              0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
 TEST_F(RefreshRateConfigsTest, getFrameRateDivider) {
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
@@ -1950,10 +2137,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
     layers[0].name = "Test layer";
@@ -1995,10 +2182,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_twoUids) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{
             LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
@@ -2035,10 +2222,10 @@
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) {
+    RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
-                                                 HWC_CONFIG_ID_120,
-                                                 /*enableFrameRateOverride=*/true);
+                                                 HWC_CONFIG_ID_120, config);
 
     auto layers = std::vector<LayerRequirement>{
             LayerRequirement{.ownerUid = 1234, .weight = 1.0f},