Merge "Make sure ANATIVEWINDOW_QUERY_BUFFER_AGE is identified as VNDK API." into sc-dev
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 8ae69de..8356005 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -87,8 +87,15 @@
         mUpdateProc(mImageCtx, context);
     }
 
+    auto colorType = mColorType;
+    if (alphaType == kOpaque_SkAlphaType) {
+        if (colorType == kRGBA_8888_SkColorType) {
+            colorType = kRGB_888x_SkColorType;
+        }
+    }
+
     sk_sp<SkImage> image =
-            SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, mColorType,
+            SkImage::MakeFromTexture(context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType,
                                      alphaType, toSkColorSpace(dataspace), releaseImageProc, this);
     if (image.get()) {
         // The following ref will be counteracted by releaseProc, when SkImage is discarded.
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 3133de6..a9e8430 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -65,6 +65,8 @@
             return mTexture->getOrCreateSurface(dataspace, context);
         }
 
+        SkColorType colorType() const { return mTexture->mColorType; }
+
         DISALLOW_COPY_AND_ASSIGN(LocalRef);
 
     private:
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 47c330f..2d80c46 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -971,11 +971,28 @@
                                                       false);
             }
 
-            sk_sp<SkImage> image =
-                    imageTextureRef->makeImage(layerDataspace,
-                                               item.usePremultipliedAlpha ? kPremul_SkAlphaType
-                                                                          : kUnpremul_SkAlphaType,
-                                               grContext);
+            // isOpaque means we need to ignore the alpha in the image,
+            // replacing it with the alpha specified by the LayerSettings. See
+            // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
+            // The proper way to do this is to use an SkColorType that ignores
+            // alpha, like kRGB_888x_SkColorType, and that is used if the
+            // incoming image is kRGBA_8888_SkColorType. However, the incoming
+            // image may be kRGBA_F16_SkColorType, for which there is no RGBX
+            // SkColorType, or kRGBA_1010102_SkColorType, for which we have
+            // kRGB_101010x_SkColorType, but it is not yet supported as a source
+            // on the GPU. (Adding both is tracked in skbug.com/12048.) In the
+            // meantime, we'll use a workaround that works unless we need to do
+            // any color conversion. The workaround requires that we pretend the
+            // image is already premultiplied, so that we do not premultiply it
+            // before applying SkBlendMode::kPlus.
+            const bool useIsOpaqueWorkaround = item.isOpaque &&
+                    (imageTextureRef->colorType() == kRGBA_1010102_SkColorType ||
+                     imageTextureRef->colorType() == kRGBA_F16_SkColorType);
+            const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType
+                    : item.isOpaque                      ? kOpaque_SkAlphaType
+                    : item.usePremultipliedAlpha         ? kPremul_SkAlphaType
+                                                         : kUnpremul_SkAlphaType;
+            sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
 
             auto texMatrix = getSkM44(item.textureTransform).asM33();
             // textureTansform was intended to be passed directly into a shader, so when
@@ -1004,27 +1021,7 @@
                 shader = image->makeShader(SkSamplingOptions(), matrix);
             }
 
-            // Handle opaque images - it's a little nonstandard how we do this.
-            // Fundamentally we need to support SurfaceControl.Builder#setOpaque:
-            // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean)
-            // The important language is that when isOpaque is set, opacity is not sampled from the
-            // alpha channel, but blending may still be supported on a transaction via setAlpha. So,
-            // here's the conundrum:
-            // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated
-            // as an internal hint - composition is undefined when there are alpha bits present.
-            // 2. We can try to lie about the pixel layout, but that only works for RGBA8888
-            // buffers, i.e., treating them as RGBx8888 instead. But we can't do the same for
-            // RGBA1010102 because RGBx1010102 is not supported as a pixel layout for SkImages. It's
-            // also not clear what to use for F16 either, and lying about the pixel layout is a bit
-            // of a hack anyways.
-            // 3. We can't change the blendmode to src, because while this satisfies the requirement
-            // for ignoring the alpha channel, it doesn't quite satisfy the blending requirement
-            // because src always clobbers the destination content.
-            //
-            // So, what we do here instead is an additive blend mode where we compose the input
-            // image with a solid black. This might need to be reassess if this does not support
-            // FP16 incredibly well, but FP16 end-to-end isn't well supported anyway at the moment.
-            if (item.isOpaque) {
+            if (useIsOpaqueWorkaround) {
                 shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
                                           SkShaders::Color(SkColors::kBlack,
                                                            toSkColorSpace(layerDataspace)));
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 1ca7a16..dfab6e8 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -54,6 +54,7 @@
     virtual std::unique_ptr<renderengine::gl::GLESRenderEngine> createGLESRenderEngine() {
         return nullptr;
     }
+    virtual bool useColorManagement() const = 0;
 };
 
 class GLESRenderEngineFactory : public RenderEngineFactory {
@@ -82,6 +83,8 @@
                         .build();
         return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
+
+    bool useColorManagement() const override { return false; }
 };
 
 class GLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -110,6 +113,8 @@
                         .build();
         return renderengine::gl::GLESRenderEngine::create(reCreationArgs);
     }
+
+    bool useColorManagement() const override { return true; }
 };
 
 class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
@@ -130,9 +135,16 @@
                         .setSupportsBackgroundBlur(true)
                         .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
                         .setRenderEngineType(type())
+                        // FIXME (b/189935602): This version is currently color managed.
+                        // We should change it and fix the tests that fail.
+                        //.setUseColorManagerment(false)
                         .build();
         return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
     }
+
+    // FIXME (b/189935602): This version is currently color managed.
+    // We should change it and fix the tests that fail.
+    bool useColorManagement() const override { return true; }
 };
 
 class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -157,6 +169,8 @@
                         .build();
         return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
     }
+
+    bool useColorManagement() const override { return true; }
 };
 
 class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -295,6 +309,7 @@
                 const uint8_t expected[4] = {r, g, b, a};
                 bool equal = colorCompare(src, expected);
                 EXPECT_TRUE(equal)
+                        << GetParam()->name().c_str() << ": "
                         << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
                         << "expected (" << static_cast<uint32_t>(r) << ", "
                         << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
@@ -2015,6 +2030,56 @@
     expectBufferColor(rect, 0, 128, 0, 128);
 }
 
+TEST_P(RenderEngineTest, test_isOpaque) {
+    initializeRenderEngine();
+
+    const auto rect = Rect(0, 0, 1, 1);
+    const renderengine::DisplaySettings display{
+            .physicalDisplay = rect,
+            .clip = rect,
+            .outputDataspace = ui::Dataspace::DISPLAY_P3,
+    };
+
+    // Create an unpremul buffer that is green with no alpha. Using isOpaque
+    // should make the green show.
+    const auto buf = allocateSourceBuffer(1, 1);
+    {
+        uint8_t* pixels;
+        buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                               reinterpret_cast<void**>(&pixels));
+        pixels[0] = 0;
+        pixels[1] = 255;
+        pixels[2] = 0;
+        pixels[3] = 0;
+        buf->getBuffer()->unlock();
+    }
+
+    const renderengine::LayerSettings greenLayer{
+            .geometry.boundaries = rect.toFloatRect(),
+            .source =
+                    renderengine::PixelSource{
+                            .buffer =
+                                    renderengine::Buffer{
+                                            .buffer = buf,
+                                            // Although the pixels are not
+                                            // premultiplied in practice, this
+                                            // matches the input we see.
+                                            .usePremultipliedAlpha = true,
+                                            .isOpaque = true,
+                                    },
+                    },
+            .alpha = 1.0f,
+    };
+
+    std::vector<const renderengine::LayerSettings*> layers{&greenLayer};
+    invokeDraw(display, layers);
+
+    if (GetParam()->useColorManagement()) {
+        expectBufferColor(rect, 117, 251, 76, 255);
+    } else {
+        expectBufferColor(rect, 0, 255, 0, 255);
+    }
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 91d2d58..3f958ba 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -128,8 +128,9 @@
     }
 
     // Ensure that layerCount is valid.
-    if (layerCount < 1)
+    if (layerCount < 1) {
         layerCount = 1;
+    }
 
     // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
     usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
@@ -140,7 +141,7 @@
         ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
               "usage %" PRIx64 ": %d",
               width, height, layerCount, format, usage, error);
-        return NO_MEMORY;
+        return error;
     }
 
     if (!importBuffer) {
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/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 953eb76..3d49183 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -344,8 +344,8 @@
     if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
         return;
     }
-    auto outputState = editState();
-    outputState.dataspace = clientTargetProperty.dataspace;
+
+    editState().dataspace = clientTargetProperty.dataspace;
     getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
     getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
 }
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/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index e12cb57..ac3ba0d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -847,6 +847,29 @@
 }
 
 /*
+ * Display::applyClientTargetRequests()
+ */
+
+using DisplayApplyClientTargetRequests = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, applyClientTargetRequests) {
+    Display::ClientTargetProperty clientTargetProperty = {
+            .pixelFormat = hal::PixelFormat::RGB_565,
+            .dataspace = hal::Dataspace::STANDARD_BT470M,
+    };
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
+    EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
+    mDisplay->applyClientTargetRequests(clientTargetProperty);
+
+    auto& state = mDisplay->getState();
+    EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+}
+
+/*
  * Display::presentAndGetFrameFences()
  */
 
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 d720bcb..9fcc17c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -587,11 +587,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 a86e061..d76c274 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1029,10 +1029,6 @@
         updatePhaseConfiguration(refreshRate.getFps());
         mScheduler->setModeChangePending(true);
     }
-
-    if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps());
-    }
 }
 
 status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
@@ -1190,6 +1186,10 @@
     }
 
     mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+    if (mRefreshRateOverlay) {
+        mRefreshRateOverlay->changeRefreshRate(desiredMode->getFps());
+    }
+
     // Scheduler will submit an empty frame to HWC if needed.
     mSetActiveModePending = true;
 }
@@ -6202,9 +6202,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);