Improve blur caching heuristic

This allows for caching the following scenario where notification shade
applies a background blur radius: If the layers behind the notification
shade are not changing, the notification shade's blur radius not
changing, but the notification shade's buffer does change, then we want
to cache the contribution of the blur radius onto the resulting
CachedSet.

Bug: 187705068
Test: Enable blur and swipe notification shade very slowly

Change-Id: I7019be4b8438b79ebb29b2a05e8860916198f18f
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 791e7db..e51019a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -78,6 +78,30 @@
     virtual void prepareCompositionState(StateSubset) = 0;
 
     struct ClientCompositionTargetSettings {
+        enum class BlurSetting {
+            Disabled,
+            BackgroundBlurOnly,
+            BlurRegionsOnly,
+            Enabled,
+        };
+
+        friend std::string toString(BlurSetting blurSetting) {
+            switch (blurSetting) {
+                case BlurSetting::Enabled:
+                    return "Enabled";
+                case BlurSetting::BlurRegionsOnly:
+                    return "BlurRegionsOnly";
+                case BlurSetting::BackgroundBlurOnly:
+                    return "BackgroundBlurOnly";
+                case BlurSetting::Disabled:
+                    return "Disabled";
+            }
+        }
+
+        friend std::ostream& operator<<(std::ostream& os, const BlurSetting& setting) {
+            return os << toString(setting);
+        }
+
         // The clip region, or visible region that is being rendered to
         const Region& clip;
 
@@ -110,8 +134,8 @@
         // This may be requested by the HWC
         const bool clearContent;
 
-        // If set to true, change the layer settings to not use any blurs.
-        const bool disableBlurs;
+        // Configure layer settings for using blurs
+        BlurSetting blurSetting;
     };
 
     // A superset of LayerSettings required by RenderEngine to compose a layer
@@ -186,6 +210,7 @@
     PrintTo(settings.dataspace, os);
     *os << "\n    .realContentIsVisible = " << settings.realContentIsVisible;
     *os << "\n    .clearContent = " << settings.clearContent;
+    *os << "\n    .blurSetting = " << settings.blurSetting;
     *os << "\n}";
 }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 3f670a1..7564c54 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -103,6 +103,13 @@
         // be visible through it. Unowned - the OutputLayer's lifetime will
         // outlast this.)
         compositionengine::OutputLayer* peekThroughLayer = nullptr;
+        // True when this layer's blur has been cached with a previous layer, so that this layer
+        // does not need to request blurring.
+        // TODO(b/188816867): support blur regions too, which are less likely to be common if a
+        // device supports cross-window blurs. Blur region support should be doable, but we would
+        // need to make sure that layer caching works well with the blur region transform passed
+        // into RenderEngine
+        bool disableBackgroundBlur = false;
     } overrideInfo;
 
     /*
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index fdcd6ab..269ddd5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -39,6 +39,7 @@
 
         const LayerState* getState() const { return mState; }
         const std::string& getName() const { return mState->getName(); }
+        int32_t getBackgroundBlurRadius() const { return mState->getBackgroundBlurRadius(); }
         Rect getDisplayFrame() const { return mState->getDisplayFrame(); }
         const Region& getVisibleRegion() const { return mState->getVisibleRegion(); }
         const sp<GraphicBuffer>& getBuffer() const {
@@ -91,6 +92,7 @@
         mTexture = nullptr;
         mOutputDataspace = ui::Dataspace::UNKNOWN;
         mDrawFence = nullptr;
+        mBlurLayer = nullptr;
 
         mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
         Region boundingRegion;
@@ -123,9 +125,13 @@
     // nothing (besides the hole punch layer) will be drawn behind it.
     void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer);
 
+    void addBackgroundBlurLayer(const CachedSet&);
+
     // Retrieve the layer that will be drawn behind this one.
     compositionengine::OutputLayer* getHolePunchLayer() const;
 
+    compositionengine::OutputLayer* getBlurLayer() const;
+
 private:
     CachedSet() = default;
 
@@ -135,6 +141,7 @@
 
     // Unowned.
     const LayerState* mHolePunchLayer = nullptr;
+    const LayerState* mBlurLayer = nullptr;
     Rect mBounds = Rect::EMPTY_RECT;
     Region mVisibleRegion;
     size_t mAge = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 213c55e..ca1d69d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -51,6 +51,11 @@
     void dump(std::string& result) const;
     void dumpLayers(std::string& result) const;
 
+    const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
+
+protected:
+    std::optional<CachedSet> mNewCachedSet;
+
 private:
     size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const;
 
@@ -72,6 +77,7 @@
             std::vector<CachedSet>::const_iterator mStart;
             std::vector<size_t> mLengths;
             const CachedSet* mHolePunchCandidate = nullptr;
+            const CachedSet* mBlurringLayer = nullptr;
 
         public:
             // Initializes a Builder a CachedSet to start from.
@@ -90,6 +96,10 @@
                 mHolePunchCandidate = holePunchCandidate;
             }
 
+            void setBlurringLayer(const CachedSet* blurringLayer) {
+                mBlurringLayer = blurringLayer;
+            }
+
             // Builds a Run instance, if a valid Run may be built.
             std::optional<Run> validateAndBuild() {
                 if (mLengths.size() <= 1) {
@@ -99,7 +109,7 @@
                 return Run(mStart,
                            std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
                                        [](size_t left, size_t right) { return left + right; }),
-                           mHolePunchCandidate);
+                           mHolePunchCandidate, mBlurringLayer);
             }
 
             void reset() { *this = {}; }
@@ -112,14 +122,19 @@
         size_t getLayerLength() const { return mLength; }
         // Gets the hole punch candidate for this Run.
         const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; }
+        const CachedSet* getBlurringLayer() const { return mBlurringLayer; }
 
     private:
         Run(std::vector<CachedSet>::const_iterator start, size_t length,
-            const CachedSet* holePunchCandidate)
-              : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {}
+            const CachedSet* holePunchCandidate, const CachedSet* blurringLayer)
+              : mStart(start),
+                mLength(length),
+                mHolePunchCandidate(holePunchCandidate),
+                mBlurringLayer(blurringLayer) {}
         const std::vector<CachedSet>::const_iterator mStart;
         const size_t mLength;
         const CachedSet* const mHolePunchCandidate;
+        const CachedSet* const mBlurringLayer;
 
         friend class Builder;
     };
@@ -138,7 +153,6 @@
     std::chrono::steady_clock::time_point mLastGeometryUpdate;
 
     std::vector<CachedSet> mLayers;
-    std::optional<CachedSet> mNewCachedSet;
 
     // Statistics
     size_t mUnflattenedDisplayCost = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index fef0dfb..0b78cb8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -231,6 +231,7 @@
     bool hasBlurBehind() const {
         return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
     }
+    int32_t getBackgroundBlurRadius() const { return mBackgroundBlurRadius.get(); }
     hardware::graphics::composer::hal::Composition getCompositionType() const {
         return mCompositionType.get();
     }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 088a400..e9a8b91 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1188,19 +1188,6 @@
                 !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
 
         if (clientComposition || clearClientComposition) {
-            compositionengine::LayerFE::ClientCompositionTargetSettings
-                    targetSettings{.clip = clip,
-                                   .needsFiltering =
-                                           layer->needsFiltering() || outputState.needsFiltering,
-                                   .isSecure = outputState.isSecure,
-                                   .supportsProtectedContent = supportsProtectedContent,
-                                   .clearRegion = clientComposition ? clearRegion : stubRegion,
-                                   .viewport = outputState.layerStackSpace.content,
-                                   .dataspace = outputDataspace,
-                                   .realContentIsVisible = realContentIsVisible,
-                                   .clearContent = !clientComposition,
-                                   .disableBlurs = disableBlurs};
-
             std::vector<LayerFE::LayerSettings> results;
             if (layer->getState().overrideInfo.buffer != nullptr) {
                 if (layer->getState().overrideInfo.buffer->getBuffer() != previousOverrideBuffer) {
@@ -1212,6 +1199,25 @@
                           layer->getLayerFE().getDebugName());
                 }
             } else {
+                LayerFE::ClientCompositionTargetSettings::BlurSetting blurSetting = disableBlurs
+                        ? LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled
+                        : (layer->getState().overrideInfo.disableBackgroundBlur
+                                   ? LayerFE::ClientCompositionTargetSettings::BlurSetting::
+                                             BlurRegionsOnly
+                                   : LayerFE::ClientCompositionTargetSettings::BlurSetting::
+                                             Enabled);
+                compositionengine::LayerFE::ClientCompositionTargetSettings
+                        targetSettings{.clip = clip,
+                                       .needsFiltering = layer->needsFiltering() ||
+                                               outputState.needsFiltering,
+                                       .isSecure = outputState.isSecure,
+                                       .supportsProtectedContent = supportsProtectedContent,
+                                       .clearRegion = clientComposition ? clearRegion : stubRegion,
+                                       .viewport = outputState.layerStackSpace.content,
+                                       .dataspace = outputDataspace,
+                                       .realContentIsVisible = realContentIsVisible,
+                                       .clearContent = !clientComposition,
+                                       .blurSetting = blurSetting};
                 results = layerFE.prepareClientCompositionList(targetSettings);
                 if (realContentIsVisible && !results.empty()) {
                     layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index b4c314c..cfa740e 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -78,6 +78,8 @@
     std::string visibleRegionString;
     overrideInfo.visibleRegion.dump(visibleRegionString, "");
     dumpVal(out, "override visible region", visibleRegionString);
+    dumpVal(out, "override peekThroughLayer", overrideInfo.peekThroughLayer);
+    dumpVal(out, "override disableBackgroundBlur", overrideInfo.disableBackgroundBlur);
 
     if (hwc) {
         dumpHwc(*hwc, out);
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index b61daeb..3cfb211 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -25,6 +25,7 @@
 #include <math/HashCombine.h>
 #include <renderengine/DisplaySettings.h>
 #include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
 
 #include <utils/Trace.h>
 
@@ -184,7 +185,7 @@
             .dataspace = outputDataspace,
             .realContentIsVisible = true,
             .clearContent = false,
-            .disableBlurs = false,
+            .blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     std::vector<renderengine::LayerSettings> layerSettings;
@@ -201,6 +202,24 @@
     std::transform(layerSettings.cbegin(), layerSettings.cend(),
                    std::back_inserter(layerSettingsPointers),
                    [](const renderengine::LayerSettings& settings) { return &settings; });
+
+    renderengine::LayerSettings blurLayerSettings;
+    if (mBlurLayer) {
+        auto blurSettings = targetSettings;
+        blurSettings.blurSetting =
+                LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly;
+        auto clientCompositionList =
+                mBlurLayer->getOutputLayer()->getLayerFE().prepareClientCompositionList(
+                        blurSettings);
+        blurLayerSettings = clientCompositionList.back();
+        // This mimics Layer::prepareClearClientComposition
+        blurLayerSettings.skipContentDraw = true;
+        blurLayerSettings.name = std::string("blur layer");
+        // Clear out the shadow settings
+        blurLayerSettings.shadow = {};
+        layerSettingsPointers.push_back(&blurLayerSettings);
+    }
+
     renderengine::LayerSettings holePunchSettings;
     if (mHolePunchLayer) {
         auto clientCompositionList =
@@ -315,10 +334,18 @@
     }
 }
 
+void CachedSet::addBackgroundBlurLayer(const CachedSet& blurLayer) {
+    mBlurLayer = blurLayer.getFirstLayer().getState();
+}
+
 compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const {
     return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr;
 }
 
+compositionengine::OutputLayer* CachedSet::getBlurLayer() const {
+    return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
+}
+
 void CachedSet::dump(std::string& result) const {
     const auto now = std::chrono::steady_clock::now();
 
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2def99d..ef46335 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -245,6 +245,12 @@
 
     auto currentLayerIter = mLayers.begin();
     auto incomingLayerIter = layers.begin();
+
+    // If not null, this represents the layer that is blurring the layer before
+    // currentLayerIter. The blurring was stored in the override buffer, so the
+    // layer that requests the blur no longer needs to do any blurring.
+    compositionengine::OutputLayer* priorBlurLayer = nullptr;
+
     while (incomingLayerIter != layers.end()) {
         if (mNewCachedSet &&
             mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) {
@@ -259,6 +265,8 @@
                     auto* peekThroughLayer = mNewCachedSet->getHolePunchLayer();
                     const size_t layerCount = currentLayerIter->getLayerCount();
                     for (size_t i = 0; i < layerCount; ++i) {
+                        bool disableBlur = priorBlurLayer &&
+                                priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
                         OutputLayer::CompositionState& state =
                                 (*incomingLayerIter)->getOutputLayer()->editState();
                         state.overrideInfo = {
@@ -270,6 +278,7 @@
                                 .damageRegion = Region::INVALID_REGION,
                                 .visibleRegion = mNewCachedSet->getVisibleRegion(),
                                 .peekThroughLayer = peekThroughLayer,
+                                .disableBackgroundBlur = disableBlur,
                         };
                         ++incomingLayerIter;
                     }
@@ -281,6 +290,7 @@
 
                     skipCount -= layerCount;
                 }
+                priorBlurLayer = mNewCachedSet->getBlurLayer();
                 merged.emplace_back(std::move(*mNewCachedSet));
                 mNewCachedSet = std::nullopt;
                 continue;
@@ -295,6 +305,8 @@
             const size_t layerCount = currentLayerIter->getLayerCount();
             auto* peekThroughLayer = currentLayerIter->getHolePunchLayer();
             for (size_t i = 0; i < layerCount; ++i) {
+                bool disableBlur =
+                        priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
                 OutputLayer::CompositionState& state =
                         (*incomingLayerIter)->getOutputLayer()->editState();
                 state.overrideInfo = {
@@ -306,6 +318,7 @@
                         .damageRegion = Region(),
                         .visibleRegion = currentLayerIter->getVisibleRegion(),
                         .peekThroughLayer = peekThroughLayer,
+                        .disableBackgroundBlur = disableBlur,
                 };
                 ++incomingLayerIter;
             }
@@ -313,15 +326,26 @@
             // Break the current layer into its constituent layers
             ++mInvalidatedCachedSetAges[currentLayerIter->getAge()];
             for (CachedSet& layer : currentLayerIter->decompose()) {
+                bool disableBlur =
+                        priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
+                OutputLayer::CompositionState& state =
+                        (*incomingLayerIter)->getOutputLayer()->editState();
+                state.overrideInfo.disableBackgroundBlur = disableBlur;
                 layer.updateAge(now);
                 merged.emplace_back(layer);
                 ++incomingLayerIter;
             }
         } else {
+            bool disableBlur =
+                    priorBlurLayer && priorBlurLayer == (*incomingLayerIter)->getOutputLayer();
+            OutputLayer::CompositionState& state =
+                    (*incomingLayerIter)->getOutputLayer()->editState();
+            state.overrideInfo.disableBackgroundBlur = disableBlur;
             currentLayerIter->updateAge(now);
             merged.emplace_back(*currentLayerIter);
             ++incomingLayerIter;
         }
+        priorBlurLayer = currentLayerIter->getBlurLayer();
         ++currentLayerIter;
     }
 
@@ -361,6 +385,15 @@
             }
         } else if (isPartOfRun) {
             builder.setHolePunchCandidate(&(*currentSet));
+
+            // If we're here then this blur layer recently had an active buffer updating, meaning
+            // that there is exactly one layer. Blur radius currently is part of layer stack
+            // geometry, so we're also guaranteed that the background blur radius hasn't changed for
+            // at least as long as this new inactive cached set.
+            if (runHasFirstLayer && layerHasBlur &&
+                currentSet->getFirstLayer().getBackgroundBlurRadius() > 0) {
+                builder.setBlurringLayer(&(*currentSet));
+            }
             if (auto run = builder.validateAndBuild(); run) {
                 runs.push_back(*run);
             }
@@ -422,6 +455,10 @@
         mNewCachedSet->append(*currentSet);
     }
 
+    if (bestRun->getBlurringLayer()) {
+        mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer());
+    }
+
     if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
         bestRun->getHolePunchCandidate()->requiresHolePunch()) {
         // Add the pip layer to mNewCachedSet, but in a special way - it should
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 6677f40..52e0428 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3784,6 +3784,46 @@
     EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
 }
 
+MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "") {
+    *result_listener << "ClientCompositionTargetSettings' BlurSettings aren't equal \n";
+    *result_listener << "expected " << expectedBlurSetting << "\n";
+    *result_listener << "actual " << arg.blurSetting << "\n";
+
+    return expectedBlurSetting == arg.blurSetting;
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, overridesBlur) {
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.overrideInfo.disableBackgroundBlur = true;
+
+    EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(*mLayers[1].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+    EXPECT_CALL(*mLayers[2].mLayerFE,
+                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                        LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+                    {mShadowSettings, mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(3u, requests.size());
+    EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
+    EXPECT_EQ(mShadowSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+    // Check that a timestamp was set for the layers that generated requests
+    EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
        onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -3867,7 +3907,7 @@
             kDisplayDataspace,
             false /* realContentIsVisible */,
             true /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -3879,7 +3919,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3923,7 +3963,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(Rect(0, 0, 30, 30)),
@@ -3935,7 +3975,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(Rect(0, 0, 40, 201)),
@@ -3947,7 +3987,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -3979,7 +4019,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -3991,7 +4031,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4003,7 +4043,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4035,7 +4075,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
 
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
@@ -4048,7 +4088,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4060,7 +4100,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4091,7 +4131,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4103,7 +4143,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4115,7 +4155,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4144,7 +4184,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
             Region(kDisplayFrame),
@@ -4156,7 +4196,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
     compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
             Region(kDisplayFrame),
@@ -4168,7 +4208,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4306,7 +4346,7 @@
             kOutputDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4324,7 +4364,7 @@
             kOutputDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4358,7 +4398,7 @@
             kDisplayDataspace,
             false /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     LayerFE::LayerSettings mShadowSettings;
@@ -4404,7 +4444,7 @@
             kDisplayDataspace,
             true /* realContentIsVisible */,
             false /* clearContent */,
-            false /* disabledBlurs */,
+            compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
     };
 
     EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 8f44677..591f981 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -24,6 +24,7 @@
 #include <renderengine/ExternalTexture.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/GraphicTypes.h>
+#include <utils/Errors.h>
 #include <memory>
 
 namespace android::compositionengine {
@@ -42,6 +43,14 @@
 
 namespace {
 
+MATCHER_P(ClientCompositionTargetSettingsBlurSettingsEq, expectedBlurSetting, "") {
+    *result_listener << "ClientCompositionTargetSettings' BlurSettings aren't equal \n";
+    *result_listener << "expected " << expectedBlurSetting << "\n";
+    *result_listener << "actual " << arg.blurSetting << "\n";
+
+    return expectedBlurSetting == arg.blurSetting;
+}
+
 class CachedSetTest : public testing::Test {
 public:
     CachedSetTest() = default;
@@ -593,5 +602,78 @@
     EXPECT_TRUE(cachedSet4.hasBlurBehind());
 }
 
+TEST_F(CachedSetTest, addBackgroundBlurLayer) {
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    CachedSet cachedSet(layer1);
+
+    EXPECT_EQ(nullptr, cachedSet.getBlurLayer());
+
+    cachedSet.addBackgroundBlurLayer(layer2);
+    EXPECT_EQ(layer2.getState()->getOutputLayer(), cachedSet.getBlurLayer());
+}
+
+TEST_F(CachedSetTest, addBlur) {
+    mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+    CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE;
+
+    CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE;
+
+    CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+    sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE;
+
+    CachedSet cachedSet(layer1);
+    cachedSet.addLayer(layer2.getState(), kStartTime + 10ms);
+
+    cachedSet.addBackgroundBlurLayer(layer3);
+
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+    clientCompList1.push_back({});
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+    clientCompList2.push_back({});
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3;
+    clientCompList3.push_back({});
+
+    clientCompList3[0].source.buffer.buffer = std::make_shared<
+            renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+                                           renderengine::ExternalTexture::READABLE);
+
+    EXPECT_CALL(*layerFE1,
+                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                        compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+                                Enabled)))
+            .WillOnce(Return(clientCompList1));
+    EXPECT_CALL(*layerFE2,
+                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                        compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+                                Enabled)))
+            .WillOnce(Return(clientCompList2));
+    EXPECT_CALL(*layerFE3,
+                prepareClientCompositionList(ClientCompositionTargetSettingsBlurSettingsEq(
+                        compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::
+                                BackgroundBlurOnly)))
+            .WillOnce(Return(clientCompList3));
+
+    const auto drawLayers = [&](const renderengine::DisplaySettings&,
+                                const std::vector<const renderengine::LayerSettings*>& layers,
+                                const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+                                base::unique_fd&&, base::unique_fd*) -> int32_t {
+        // If the highlight layer is enabled, it will increase the size by 1.
+        // We're interested in the third layer either way.
+        EXPECT_GE(layers.size(), 3u);
+        const auto* blurSettings = layers[2];
+        EXPECT_TRUE(blurSettings->skipContentDraw);
+        EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor);
+        EXPECT_EQ(0.0f, blurSettings->alpha);
+
+        return NO_ERROR;
+    };
+
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+    cachedSet.render(mRenderEngine, mOutputState);
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 7ec2c98..8b03964 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -27,6 +27,7 @@
 
 namespace android::compositionengine {
 using namespace std::chrono_literals;
+using impl::planner::CachedSet;
 using impl::planner::Flattener;
 using impl::planner::LayerState;
 using impl::planner::NonBufferHash;
@@ -43,9 +44,15 @@
 
 namespace {
 
+class TestableFlattener : public Flattener {
+public:
+    TestableFlattener(bool enableHolePunch) : Flattener(enableHolePunch) {}
+    const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
+};
+
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {}
+    FlattenerTest() : mFlattener(std::make_unique<TestableFlattener>(true)) {}
     void SetUp() override;
 
 protected:
@@ -55,7 +62,7 @@
 
     // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
     renderengine::mock::RenderEngine mRenderEngine;
-    std::unique_ptr<Flattener> mFlattener;
+    std::unique_ptr<TestableFlattener> mFlattener;
 
     const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
     std::chrono::steady_clock::time_point mTime = kStartTime;
@@ -786,6 +793,56 @@
     EXPECT_EQ(overrideBuffer3, overrideBuffer4);
 }
 
+TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiveBehindLayers) {
+    auto& layerState1 = mTestLayers[0]->layerState;
+    auto& layerState2 = mTestLayers[1]->layerState;
+
+    auto& layerStateWithBlurBehind = mTestLayers[2]->layerState;
+    mTestLayers[2]->layerFECompositionState.backgroundBlurRadius = 1;
+    layerStateWithBlurBehind->update(&mTestLayers[2]->outputLayer);
+    const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+    const auto& blurOverrideBuffer =
+            layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer;
+
+    const std::vector<const LayerState*> layers = {
+            layerState1.get(),
+            layerState2.get(),
+            layerStateWithBlurBehind.get(),
+    };
+
+    initializeFlattener(layers);
+
+    // Mark the first two layers inactive, but update the blur layer
+    mTime += 200ms;
+    layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
+
+    // layers would be flattened but the buffer would not be overridden
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+
+    initializeOverrideBuffer(layers);
+    EXPECT_EQ(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+
+    const auto& cachedSet = mFlattener->getNewCachedSetForTesting();
+    ASSERT_NE(std::nullopt, cachedSet);
+    EXPECT_EQ(&mTestLayers[2]->outputLayer, cachedSet->getBlurLayer());
+
+    for (const auto layer : layers) {
+        EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
+    }
+
+    // the new flattened layer is replaced
+    initializeOverrideBuffer(layers);
+    EXPECT_NE(getNonBufferHash(layers),
+              mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+    mFlattener->renderCachedSets(mRenderEngine, mOutputState);
+    EXPECT_NE(nullptr, overrideBuffer1);
+    EXPECT_EQ(overrideBuffer2, overrideBuffer1);
+    EXPECT_EQ(nullptr, blurOverrideBuffer);
+}
+
 TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) {
     auto& layerState1 = mTestLayers[0]->layerState;
     auto& layerState2 = mTestLayers[1]->layerState;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a7c8704..45bacad 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -586,11 +586,24 @@
 
     layerSettings.alpha = alpha;
     layerSettings.sourceDataspace = getDataSpace();
-    if (!targetSettings.disableBlurs) {
-        layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
-        layerSettings.blurRegions = getBlurRegions();
-        layerSettings.blurRegionTransform =
-                getActiveTransform(getDrawingState()).inverse().asMatrix4();
+    switch (targetSettings.blurSetting) {
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
+            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+            layerSettings.blurRegions = getBlurRegions();
+            layerSettings.blurRegionTransform =
+                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BackgroundBlurOnly:
+            layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly:
+            layerSettings.blurRegions = getBlurRegions();
+            layerSettings.blurRegionTransform =
+                    getActiveTransform(getDrawingState()).inverse().asMatrix4();
+            break;
+        case LayerFE::ClientCompositionTargetSettings::BlurSetting::Disabled:
+        default:
+            break;
     }
     layerSettings.stretchEffect = getStretchEffect();
     // Record the name of the layer for debugging further down the stack.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 881ee5b..be597bd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6200,9 +6200,12 @@
                 clearRegion,
                 layerStackSpaceRect,
                 clientCompositionDisplay.outputDataspace,
-                true, /* realContentIsVisible */
+                true,  /* realContentIsVisible */
                 false, /* clearContent */
-                disableBlurs,
+                disableBlurs ? compositionengine::LayerFE::ClientCompositionTargetSettings::
+                                       BlurSetting::Disabled
+                             : compositionengine::LayerFE::ClientCompositionTargetSettings::
+                                       BlurSetting::Enabled,
         };
         std::vector<compositionengine::LayerFE::LayerSettings> results =
                 layer->prepareClientCompositionList(targetSettings);