Merge "Use updated AppOps API in SensorService"
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index bbf4f8d..c6f656b 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -54,6 +54,7 @@
         ReparentChildrenChange      reparent_children       = 20;
         BackgroundBlurRadiusChange  background_blur_radius  = 21;
         ShadowRadiusChange          shadow_radius           = 22;
+        BlurRegionsChange           blur_regions            = 23;
     }
 }
 
@@ -207,6 +208,23 @@
     required float radius = 1;
 }
 
+message BlurRegionsChange {
+    repeated BlurRegionChange blur_regions = 1;
+}
+
+message BlurRegionChange {
+    required uint32 blur_radius = 1;
+    required float corner_radius_tl = 2;
+    required float corner_radius_tr = 3;
+    required float corner_radius_bl = 4;
+    required float corner_radius_br = 5;
+    required float alpha = 6;
+    required int32 left = 7;
+    required int32 top = 8;
+    required int32 right = 9;
+    required int32 bottom = 10;
+}
+
 message Origin {
     required int32 pid = 1;
     required int32 uid = 2;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index c186801..5849212 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -423,6 +423,9 @@
             case SurfaceChange::SurfaceChangeCase::kShadowRadius:
                 setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+                setBlurRegionsChange(transaction, change.id(), change.blur_regions());
+                break;
             default:
                 status = 1;
                 break;
@@ -728,3 +731,24 @@
         layer_id id, const ShadowRadiusChange& c) {
     t.setShadowRadius(mLayers[id], c.radius());
 }
+
+void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const BlurRegionsChange& c) {
+    std::vector<BlurRegion> regions;
+    for(size_t i=0; i < c.blur_regions_size(); i++) {
+        auto protoRegion = c.blur_regions(i);
+        regions.push_back(BlurRegion{
+            .blurRadius = protoRegion.blur_radius(),
+            .alpha = protoRegion.alpha(),
+            .cornerRadiusTL = protoRegion.corner_radius_tl(),
+            .cornerRadiusTR = protoRegion.corner_radius_tr(),
+            .cornerRadiusBL = protoRegion.corner_radius_bl(),
+            .cornerRadiusBR = protoRegion.corner_radius_br(),
+            .left = protoRegion.left(),
+            .top = protoRegion.top(),
+            .right = protoRegion.right(),
+            .bottom = protoRegion.bottom()
+        });
+    }
+    t.setBlurRegions(mLayers[id], regions);
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index e439718..a22262a 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -96,6 +96,8 @@
             layer_id id, const CornerRadiusChange& cc);
     void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
             layer_id id, const BackgroundBlurRadiusChange& cc);
+    void setBlurRegions(SurfaceComposerClient::Transaction& t,
+            layer_id id, const BlurRegionsChange& cc);
     void setMatrix(SurfaceComposerClient::Transaction& t,
             layer_id id, const MatrixChange& mc);
     void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
@@ -120,6 +122,8 @@
             layer_id id, const ReparentChildrenChange& c);
     void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
             layer_id id, const ShadowRadiusChange& c);
+    void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const BlurRegionsChange& c);
 
     void setDisplaySurface(SurfaceComposerClient::Transaction& t,
             display_id id, const DispSurfaceChange& dsc);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 4333126..9722f36 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -147,6 +147,20 @@
     SAFE_PARCEL(output.writeUint32, fixedTransformHint);
     SAFE_PARCEL(output.writeUint64, frameNumber);
     SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
+
+    SAFE_PARCEL(output.writeUint32, blurRegions.size());
+    for (auto region : blurRegions) {
+        SAFE_PARCEL(output.writeUint32, region.blurRadius);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL);
+        SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR);
+        SAFE_PARCEL(output.writeFloat, region.alpha);
+        SAFE_PARCEL(output.writeInt32, region.left);
+        SAFE_PARCEL(output.writeInt32, region.top);
+        SAFE_PARCEL(output.writeInt32, region.right);
+        SAFE_PARCEL(output.writeInt32, region.bottom);
+    }
     return NO_ERROR;
 }
 
@@ -252,6 +266,24 @@
     fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
     SAFE_PARCEL(input.readUint64, &frameNumber);
     SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId);
+
+    uint32_t numRegions = 0;
+    SAFE_PARCEL(input.readUint32, &numRegions);
+    blurRegions.clear();
+    for (uint32_t i = 0; i < numRegions; i++) {
+        BlurRegion region;
+        SAFE_PARCEL(input.readUint32, &region.blurRadius);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusTR);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBL);
+        SAFE_PARCEL(input.readFloat, &region.cornerRadiusBR);
+        SAFE_PARCEL(input.readFloat, &region.alpha);
+        SAFE_PARCEL(input.readInt32, &region.left);
+        SAFE_PARCEL(input.readInt32, &region.top);
+        SAFE_PARCEL(input.readInt32, &region.right);
+        SAFE_PARCEL(input.readInt32, &region.bottom);
+        blurRegions.push_back(region);
+    }
     return NO_ERROR;
 }
 
@@ -375,6 +407,10 @@
         what |= eBackgroundBlurRadiusChanged;
         backgroundBlurRadius = other.backgroundBlurRadius;
     }
+    if (other.what & eBlurRegionsChanged) {
+        what |= eBlurRegionsChanged;
+        blurRegions = other.blurRegions;
+    }
     if (other.what & eDeferTransaction_legacy) {
         what |= eDeferTransaction_legacy;
         barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 32f9695..039e900 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1017,6 +1017,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions(
+        const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eBlurRegionsChanged;
+    s->blurRegions = blurRegions;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
         const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ff3a87d..a73d9a6 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -51,6 +51,7 @@
 #include <gui/LayerMetadata.h>
 #include <gui/SurfaceControl.h>
 #include <math/vec3.h>
+#include <ui/BlurRegion.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -128,6 +129,7 @@
         eFixedTransformHintChanged = 0x200'00000000,
         eFrameNumberChanged = 0x400'00000000,
         eFrameTimelineVsyncChanged = 0x800'00000000,
+        eBlurRegionsChanged = 0x1000'00000000,
     };
 
     layer_state_t();
@@ -186,6 +188,7 @@
     int32_t api;
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
+    std::vector<BlurRegion> blurRegions;
 
 #ifndef NO_INPUT
     sp<InputWindowHandle> inputHandle = new InputWindowHandle();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 13abed2..73909a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -29,6 +29,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/BlurRegion.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
@@ -439,6 +440,8 @@
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
         Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
                                              int backgroundBlurRadius);
+        Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
+                                    const std::vector<BlurRegion>& regions);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
         Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
         // Defers applying any changes made in this transaction until the Layer
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 95e9367..d8d989e 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -21,6 +21,7 @@
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/Texture.h>
+#include <ui/BlurRegion.h>
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
@@ -151,6 +152,8 @@
     ShadowSettings shadow;
 
     int backgroundBlurRadius = 0;
+
+    std::vector<BlurRegion> blurRegions;
 };
 
 // Keep in sync with custom comparison function in
@@ -182,7 +185,29 @@
             lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
 }
 
+static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
+            lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
+            lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
+            lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
+            lhs.bottom == rhs.bottom;
+}
+
+static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
+    return !(lhs == rhs);
+}
+
 static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+    if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
+        return false;
+    }
+    const auto size = lhs.blurRegions.size();
+    for (size_t i = 0; i < size; i++) {
+        if (lhs.blurRegions[i] != rhs.blurRegions[i]) {
+            return false;
+        }
+    }
+
     return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
             lhs.sourceDataspace == rhs.sourceDataspace &&
             lhs.colorTransform == rhs.colorTransform &&
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 69ad189..03c8e80 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -23,6 +23,14 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <GLES2/gl2.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+
 #include <GrContextOptions.h>
 #include <SkCanvas.h>
 #include <SkColorFilter.h>
@@ -500,10 +508,25 @@
         SkPaint paint;
         const auto& bounds = layer->geometry.boundaries;
         const auto dest = getSkRect(bounds);
+        std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs;
 
-        if (layer->backgroundBlurRadius > 0) {
-            ATRACE_NAME("BackgroundBlur");
-            mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+        if (mBlurFilter) {
+            if (layer->backgroundBlurRadius > 0) {
+                ATRACE_NAME("BackgroundBlur");
+                auto blurredSurface =
+                        mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+                cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+            }
+            if (layer->blurRegions.size() > 0) {
+                for (auto region : layer->blurRegions) {
+                    if (cachedBlurs[region.blurRadius]) {
+                        continue;
+                    }
+                    ATRACE_NAME("BlurRegion");
+                    auto blurredSurface = mBlurFilter->generate(canvas, surface, region.blurRadius);
+                    cachedBlurs[region.blurRadius] = blurredSurface;
+                }
+            }
         }
 
         if (layer->source.buffer.buffer) {
@@ -571,7 +594,11 @@
         } else {
             ATRACE_NAME("DrawColor");
             const auto color = layer->source.solidColor;
-            paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
+            paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
+                                                       .fG = color.g,
+                                                       .fB = color.b,
+                                                       layer->alpha},
+                                             nullptr));
         }
 
         paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
@@ -580,6 +607,10 @@
         canvas->save();
         canvas->concat(getSkM44(layer->geometry.positionTransform));
 
+        for (const auto effectRegion : layer->blurRegions) {
+            drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]);
+        }
+
         if (layer->shadow.length > 0) {
             const auto rect = layer->geometry.roundedCornersRadius > 0
                     ? getSkRect(layer->geometry.roundedCornersCrop)
@@ -592,6 +623,7 @@
         } else {
             canvas->drawRect(dest, paint);
         }
+
         canvas->restore();
     }
     {
@@ -678,6 +710,34 @@
                               flags);
 }
 
+void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+                                        const SkRect& layerBoundaries,
+                                        sk_sp<SkSurface> blurredSurface) {
+    ATRACE_CALL();
+    SkPaint paint;
+    paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
+    const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+                                       effectRegion.bottom);
+
+    const auto matrix = mBlurFilter->getShaderMatrix(
+            SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top()));
+    paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix));
+
+    if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+        effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+        const SkVector radii[4] =
+                {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+                 SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+                 SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+                 SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+        SkRRect roundedRect;
+        roundedRect.setRectRadii(rect, radii);
+        canvas->drawRRect(roundedRect, paint);
+    } else {
+        canvas->drawRect(rect, paint);
+    }
+}
+
 EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
                                                 EGLContext shareContext, bool useContextPriority,
                                                 Protection protection) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ed4ba11..0143445 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -75,6 +75,8 @@
     bool waitFence(base::unique_fd fenceFd);
     void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
                     const ShadowSettings& shadowSettings);
+    void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds,
+                        sk_sp<SkSurface> blurrendSurface);
 
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 8704b3a..f6a316f 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -55,8 +55,10 @@
     mBlurEffect = std::move(blurEffect);
 }
 
-void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
+sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                      const uint32_t blurRadius) const {
     ATRACE_CALL();
+
     // Kawase is an approximation of Gaussian, but it behaves differently from it.
     // A radius transformation is required for approximating them, and also to introduce
     // non-integer steps, necessary to smoothly interpolate large radii.
@@ -111,25 +113,35 @@
             drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
 
             // Swap buffers for next iteration
-            auto tmp = drawSurface;
+            const auto tmp = drawSurface;
             drawSurface = readSurface;
             readSurface = tmp;
             blurBuilder.child("input") = nullptr;
         }
         lastDrawTarget = readSurface;
     }
+    lastDrawTarget->flushAndSubmit();
+    return lastDrawTarget;
+}
 
-    drawSurface->flushAndSubmit();
+sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                                  const uint32_t blurRadius) const {
+    ATRACE_CALL();
+    auto surface = generate(canvas, input, blurRadius);
 
-    // do the final composition, with alpha blending to hide downscaling artifacts.
-    {
-        SkPaint paint;
-        paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
-                SkMatrix::MakeScale(kInverseInputScale)));
-        paint.setFilterQuality(kLow_SkFilterQuality);
-        paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
-        canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
-    }
+    SkPaint paint;
+    const auto image = surface->makeImageSnapshot();
+    paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale)));
+    paint.setFilterQuality(kLow_SkFilterQuality);
+    paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+    canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
+    return surface;
+}
+
+SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const {
+    SkMatrix matrix;
+    matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale));
+    return matrix;
 }
 
 } // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 94b3673..6f973d7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -47,8 +47,14 @@
     explicit BlurFilter();
     virtual ~BlurFilter(){};
 
-    // Execute blur passes, rendering to a canvas.
-    void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+    // Execute blur, saving it to a texture
+    sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                              const uint32_t radius) const;
+    // Same as generate but also drawing to the screen
+    sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+                          const uint32_t radius) const;
+    // Returns a matrix that should be applied to the blur shader
+    SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const;
 
 private:
     sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h
new file mode 100644
index 0000000..c5a5d47
--- /dev/null
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+namespace android {
+
+struct BlurRegion {
+    uint32_t blurRadius;
+    float cornerRadiusTL;
+    float cornerRadiusTR;
+    float cornerRadiusBL;
+    float cornerRadiusBR;
+    float alpha;
+    int left;
+    int top;
+    int right;
+    int bottom;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
index afde5f7..f5c5a5a 100644
--- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -52,7 +52,7 @@
 
 // Fuzzer for Serialization operations, this is mostly just lifted from the
 // existing test cases to use fuzzed values as inputs.
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+void FuzzSerializeDeserialize(const uint8_t* data, size_t size) {
   FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
   Payload result;
 
@@ -106,6 +106,183 @@
   Deserialize(&vec_val, &result);
   Serialize(t1_val, &result);
   Deserialize(&t1_val, &result);
+}
+
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+  std::uint8_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::uint16_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::uint32_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {
+      ENCODING_TYPE_UINT64,           fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>()};
+  std::uint64_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+  std::int8_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int16_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int32_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT64,
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  std::int64_t result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  float floatResult;
+  Deserialize(&floatResult, &buffer);
+
+  buffer.Rewind();
+  double doubleResult;
+  Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {
+      ENCODING_TYPE_FLOAT64,          fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+      fdp.ConsumeIntegral<uint8_t>()};
+  double result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  std::string s_val = fdp.ConsumeRemainingBytesAsString();
+  Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+  for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+       iter++) {
+    buffer.Append(1, *iter);
+  }
+  std::string result;
+  Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+  // Fill the map with the fuzzed data, not attempting to
+  // make a valid map
+  while (fdp.remaining_bytes() > 0) {
+    buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+  }
+
+  std::map<std::uint32_t, std::uint32_t> result;
+  Deserialize(&result, &buffer);
+
+  buffer.Rewind();
+  std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+  Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+  Payload buffer = {ENCODING_TYPE_INT16,
+                    ENCODING_TYPE_FLOAT32,
+                    ENCODING_TYPE_FIXSTR_MAX,
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>(),
+                    fdp.ConsumeIntegral<uint8_t>()};
+  // Add the rest of the data as a string
+  std::string s_val = fdp.ConsumeRemainingBytesAsString();
+  for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+       iter++) {
+    buffer.Append(1, *iter);
+  }
+  Variant<int, float, std::string> result;
+  Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+  FuzzDeserializeUint8(data, size);
+  FuzzDeserializeUint16(data, size);
+  FuzzDeserializeUint32(data, size);
+  FuzzDeserializeUint64(data, size);
+  FuzzDeserializeInt8(data, size);
+  FuzzDeserializeInt16(data, size);
+  FuzzDeserializeInt32(data, size);
+  FuzzDeserializeInt64(data, size);
+  FuzzDeserializeFloat32(data, size);
+  FuzzDeserializeFloat64(data, size);
+  FuzzDeserializeFixstr(data, size);
+  FuzzDeserializeFixmap(data, size);
+  FuzzDeserializeVariant(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  FuzzSerializeDeserialize(data, size);
+  FuzzDeserialize(data, size);
 
   return 0;
 }
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 9c3fdbb..d302f98 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -204,8 +204,8 @@
     layer.frameNumber = mCurrentFrameNumber;
     layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
 
-    // TODO: we could be more subtle with isFixedSize()
-    const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
+    const bool useFiltering =
+            targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
 
     // Query the texture matrix given our current filtering mode.
     float textureMatrix[16];
@@ -822,6 +822,10 @@
     }
 }
 
+bool BufferLayer::bufferNeedsFiltering() const {
+    return isFixedSize();
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index e6a0f81..deaf846 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -115,6 +115,10 @@
 
     ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
+    // Returns true if the transformed buffer size does not match the layer size and we need
+    // to apply filtering.
+    virtual bool bufferNeedsFiltering() const;
+
 protected:
     struct BufferInfo {
         nsecs_t mDesiredPresentTime;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index ec828d4..9ab2974 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -442,7 +442,8 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName,
+                                                                     mFrameTimelineVsyncId);
         surfaceFrame->setActualQueueTime(systemTime());
 
         mQueueItems.push_back({item, std::move(surfaceFrame)});
@@ -480,7 +481,8 @@
         }
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerUid, mName, mName,
+                                                                     mFrameTimelineVsyncId);
         surfaceFrame->setActualQueueTime(systemTime());
         mQueueItems[mQueueItems.size() - 1].item = item;
         mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 6f344c7..a64b243 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -277,7 +277,7 @@
 
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
-                                      postTime);
+                                      mOwnerUid, postTime);
     desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
@@ -744,6 +744,31 @@
                                         static_cast<float>(s.active.transform.ty() + s.active.h)),
                               radius);
 }
+
+bool BufferStateLayer::bufferNeedsFiltering() const {
+    const State& s(getDrawingState());
+    if (!s.buffer) {
+        return false;
+    }
+
+    uint32_t bufferWidth = s.buffer->width;
+    uint32_t bufferHeight = s.buffer->height;
+
+    // Undo any transformations on the buffer and return the result.
+    if (s.transform & ui::Transform::ROT_90) {
+        std::swap(bufferWidth, bufferHeight);
+    }
+
+    if (s.transformToDisplayInverse) {
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        if (invTransform & ui::Transform::ROT_90) {
+            std::swap(bufferWidth, bufferHeight);
+        }
+    }
+
+    const Rect layerSize{getBounds()};
+    return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 4773286..104a13b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -143,6 +143,8 @@
 
     bool willPresentCurrentTransaction() const;
 
+    bool bufferNeedsFiltering() const override;
+
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 77400eb..5a3b9ac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -20,6 +20,7 @@
 
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
+#include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -118,6 +119,9 @@
     // length of the shadow in screen space
     float shadowRadius{0.f};
 
+    // List of regions that require blur
+    std::vector<BlurRegion> blurRegions;
+
     /*
      * Geometry state
      */
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index c625b2e..3852f45 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -669,7 +669,8 @@
 compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
     compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
     for (auto* layer : getOutputLayersOrderedByZ()) {
-        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0 ||
+            layer->getLayerFE().getCompositionState()->blurRegions.size() > 0) {
             layerRequestingBgComposition = layer;
         }
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 3dd1839..9badb99 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -4014,6 +4014,34 @@
     mOutput->updateAndWriteCompositionState(args);
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    // Layer requesting blur, or below, should request client composition.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    BlurRegion region;
+    layer2.layerFEState.blurRegions.push_back(region);
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
 TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
     // In split-screen landscape mode, the screen is rotated 90 degrees, with
     // one layer on the left covering the left side of the output, and one layer
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 6ba4c43..e075d3e 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -5,10 +5,12 @@
         "FrameTimeline.cpp",
     ],
     shared_libs: [
+        "android.hardware.graphics.composer@2.4",
         "libbase",
         "libcutils",
         "liblog",
         "libgui",
+        "libtimestats",
         "libui",
         "libutils",
     ],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 43176a3..996479c 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -93,19 +93,19 @@
     }
 }
 
-std::string toString(JankType jankType) {
+std::string toString(TimeStats::JankType jankType) {
     switch (jankType) {
-        case JankType::None:
+        case TimeStats::JankType::None:
             return "None";
-        case JankType::Display:
+        case TimeStats::JankType::Display:
             return "Composer/Display - outside SF and App";
-        case JankType::SurfaceFlingerDeadlineMissed:
+        case TimeStats::JankType::SurfaceFlingerDeadlineMissed:
             return "SurfaceFlinger Deadline Missed";
-        case JankType::AppDeadlineMissed:
+        case TimeStats::JankType::AppDeadlineMissed:
             return "App Deadline Missed";
-        case JankType::PredictionExpired:
+        case TimeStats::JankType::PredictionExpired:
             return "Prediction Expired";
-        case JankType::SurfaceFlingerEarlyLatch:
+        case TimeStats::JankType::SurfaceFlingerEarlyLatch:
             return "SurfaceFlinger Early Latch";
         default:
             return "Unclassified";
@@ -177,15 +177,18 @@
     }
 }
 
-SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+SurfaceFrame::SurfaceFrame(uid_t ownerUid, std::string layerName, std::string debugName,
+                           PredictionState predictionState,
                            frametimeline::TimelineItem&& predictions)
-      : mLayerName(layerName),
+      : mOwnerUid(ownerUid),
+        mLayerName(std::move(layerName)),
+        mDebugName(std::move(debugName)),
         mPresentState(PresentState::Unknown),
         mPredictionState(predictionState),
         mPredictions(predictions),
         mActuals({0, 0, 0}),
         mActualQueueTime(0),
-        mJankType(JankType::None),
+        mJankType(TimeStats::JankType::None),
         mJankMetadata(0) {}
 
 void SurfaceFrame::setPresentState(PresentState state) {
@@ -227,17 +230,25 @@
     mActuals.presentTime = presentTime;
 }
 
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
+void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) {
     std::lock_guard<std::mutex> lock(mMutex);
     mJankType = jankType;
     mJankMetadata = jankMetadata;
 }
 
-JankType SurfaceFrame::getJankType() const {
+TimeStats::JankType SurfaceFrame::getJankType() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mJankType;
 }
 
+uid_t SurfaceFrame::getOwnerUid() const {
+    return mOwnerUid;
+}
+
+const std::string& SurfaceFrame::getName() const {
+    return mLayerName;
+}
+
 nsecs_t SurfaceFrame::getBaseTime() const {
     std::lock_guard<std::mutex> lock(mMutex);
     nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
@@ -267,8 +278,8 @@
 void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
     std::lock_guard<std::mutex> lock(mMutex);
     StringAppendF(&result, "%s", indent.c_str());
-    StringAppendF(&result, "Layer - %s", mLayerName.c_str());
-    if (mJankType != JankType::None) {
+    StringAppendF(&result, "Layer - %s", mDebugName.c_str());
+    if (mJankType != TimeStats::JankType::None) {
         // Easily identify a janky Surface Frame in the dump
         StringAppendF(&result, " [*] ");
     }
@@ -285,33 +296,35 @@
     dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
 }
 
-FrameTimeline::FrameTimeline()
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
       : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
-        mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
+        mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+        mTimeStats(std::move(timeStats)) {}
 
 FrameTimeline::DisplayFrame::DisplayFrame()
       : surfaceFlingerPredictions(TimelineItem()),
         surfaceFlingerActuals(TimelineItem()),
         predictionState(PredictionState::None),
-        jankType(JankType::None),
+        jankType(TimeStats::JankType::None),
         jankMetadata(0) {
     this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
 }
 
 std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
-        const std::string& layerName, std::optional<int64_t> token) {
+        uid_t uid, std::string layerName, std::string debugName, std::optional<int64_t> token) {
     ATRACE_CALL();
     if (!token) {
-        return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
-                                                    TimelineItem());
+        return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName),
+                                                    PredictionState::None, TimelineItem());
     }
     std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
     if (predictions) {
-        return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+        return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName),
+                                                    PredictionState::Valid,
                                                     std::move(*predictions));
     }
-    return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
-                                                TimelineItem());
+    return std::make_unique<impl::SurfaceFrame>(uid, std::move(layerName), std::move(debugName),
+                                                PredictionState::Expired, TimelineItem());
 }
 
 void FrameTimeline::addSurfaceFrame(
@@ -359,6 +372,7 @@
             }
         }
         if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+            int32_t totalJankReasons = TimeStats::JankType::None;
             auto& displayFrame = pendingPresentFence.second;
             displayFrame->surfaceFlingerActuals.presentTime = signalTime;
 
@@ -377,21 +391,26 @@
                     displayFrame->jankMetadata |= EarlyFinish;
                 }
 
-                if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
-                    displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
-                } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
-                    displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+                if ((displayFrame->jankMetadata & EarlyFinish) &&
+                    (displayFrame->jankMetadata & EarlyPresent)) {
+                    displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
+                } else if ((displayFrame->jankMetadata & LateFinish) &&
+                           (displayFrame->jankMetadata & LatePresent)) {
+                    displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed;
                 } else if (displayFrame->jankMetadata & EarlyPresent ||
                            displayFrame->jankMetadata & LatePresent) {
                     // Cases where SF finished early but frame was presented late and vice versa
-                    displayFrame->jankType = JankType::Display;
+                    displayFrame->jankType = TimeStats::JankType::Display;
                 }
             }
+
             if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
                 displayFrame->jankMetadata |=
                         sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
             }
 
+            totalJankReasons |= displayFrame->jankType;
+
             for (auto& surfaceFrame : displayFrame->surfaceFrames) {
                 if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
                     // Only presented SurfaceFrames need to be updated
@@ -401,13 +420,13 @@
                     const auto& predictionState = surfaceFrame->getPredictionState();
                     if (predictionState == PredictionState::Expired) {
                         // Jank analysis cannot be done on apps that don't use predictions
-                        surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+                        surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
                         continue;
                     } else if (predictionState == PredictionState::Valid) {
                         const auto& actuals = surfaceFrame->getActuals();
                         const auto& predictions = surfaceFrame->getPredictions();
                         int32_t jankMetadata = 0;
-                        JankType jankType = JankType::None;
+                        TimeStats::JankType jankType = TimeStats::JankType::None;
                         if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
                             jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
                                                                                   : EarlyFinish;
@@ -419,19 +438,26 @@
                                     : EarlyPresent;
                         }
                         if (jankMetadata & EarlyPresent) {
-                            jankType = JankType::SurfaceFlingerEarlyLatch;
+                            jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
                         } else if (jankMetadata & LatePresent) {
                             if (jankMetadata & EarlyFinish) {
                                 // TODO(b/169890654): Classify this properly
-                                jankType = JankType::Display;
+                                jankType = TimeStats::JankType::Display;
                             } else {
-                                jankType = JankType::AppDeadlineMissed;
+                                jankType = TimeStats::JankType::AppDeadlineMissed;
                             }
                         }
+
+                        totalJankReasons |= jankType;
+                        mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
+                                                         surfaceFrame->getName(),
+                                                         jankType | displayFrame->jankType);
                         surfaceFrame->setJankInfo(jankType, jankMetadata);
                     }
                 }
             }
+
+            mTimeStats->incrementJankyFrames(totalJankReasons);
         }
 
         mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -467,7 +493,7 @@
 void FrameTimeline::dumpDisplayFrame(std::string& result,
                                      const std::shared_ptr<DisplayFrame>& displayFrame,
                                      nsecs_t baseTime) {
-    if (displayFrame->jankType != JankType::None) {
+    if (displayFrame->jankType != TimeStats::JankType::None) {
         // Easily identify a janky Display Frame in the dump
         StringAppendF(&result, " [*] ");
     }
@@ -501,11 +527,11 @@
     nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
     for (size_t i = 0; i < mDisplayFrames.size(); i++) {
         const auto& displayFrame = mDisplayFrames[i];
-        if (displayFrame->jankType == JankType::None) {
+        if (displayFrame->jankType == TimeStats::JankType::None) {
             // Check if any Surface Frame has been janky
             bool isJanky = false;
             for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
-                if (surfaceFrame->getJankType() != JankType::None) {
+                if (surfaceFrame->getJankType() != TimeStats::JankType::None) {
                     isJanky = true;
                     break;
                 }
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index bd637df..33821e5 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,9 +16,7 @@
 
 #pragma once
 
-#include <deque>
-#include <mutex>
-
+#include <../TimeStats/TimeStats.h>
 #include <gui/ISurfaceComposer.h>
 #include <ui/FenceTime.h>
 #include <utils/RefBase.h>
@@ -26,26 +24,10 @@
 #include <utils/Timers.h>
 #include <utils/Vector.h>
 
-namespace android::frametimeline {
+#include <deque>
+#include <mutex>
 
-/*
- * The type of jank that is associated with a Display/Surface frame
- */
-enum class JankType {
-    // No Jank
-    None,
-    // Jank not related to SurfaceFlinger or the App
-    Display,
-    // SF took too long on the CPU
-    SurfaceFlingerDeadlineMissed,
-    // Either App or GPU took too long on the frame
-    AppDeadlineMissed,
-    // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
-    // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
-    PredictionExpired,
-    // Latching a buffer early might cause an early present of the frame
-    SurfaceFlingerEarlyLatch,
-};
+namespace android::frametimeline {
 
 enum JankMetadata {
     // Frame was presented earlier than expected
@@ -147,8 +129,10 @@
 
     // Create a new surface frame, set the predictions based on a token and return it to the caller.
     // Sets the PredictionState of SurfaceFrame.
+    // Debug name is the human-readable debugging string for dumpsys.
     virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
-            const std::string& layerName, std::optional<int64_t> token) = 0;
+            uid_t uid, std::string layerName, std::string debugName,
+            std::optional<int64_t> token) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
@@ -206,8 +190,8 @@
 
 class SurfaceFrame : public android::frametimeline::SurfaceFrame {
 public:
-    SurfaceFrame(const std::string& layerName, PredictionState predictionState,
-                 TimelineItem&& predictions);
+    SurfaceFrame(uid_t uid, std::string layerName, std::string debugName,
+                 PredictionState predictionState, TimelineItem&& predictions);
     ~SurfaceFrame() = default;
 
     TimelineItem getPredictions() const override { return mPredictions; };
@@ -221,32 +205,37 @@
     void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
     void setPresentState(PresentState state) override;
     void setActualPresentTime(nsecs_t presentTime);
-    void setJankInfo(JankType jankType, int32_t jankMetadata);
-    JankType getJankType() const;
+    void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata);
+    TimeStats::JankType getJankType() const;
     nsecs_t getBaseTime() const;
+    uid_t getOwnerUid() const;
+    const std::string& getName() const;
     // All the timestamps are dumped relative to the baseTime
     void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
 
 private:
+    const uid_t mOwnerUid;
     const std::string mLayerName;
+    const std::string mDebugName;
     PresentState mPresentState GUARDED_BY(mMutex);
     const PredictionState mPredictionState;
     const TimelineItem mPredictions;
     TimelineItem mActuals GUARDED_BY(mMutex);
     nsecs_t mActualQueueTime GUARDED_BY(mMutex);
     mutable std::mutex mMutex;
-    JankType mJankType GUARDED_BY(mMutex);    // Enum for the type of jank
+    TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
     int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
 };
 
 class FrameTimeline : public android::frametimeline::FrameTimeline {
 public:
-    FrameTimeline();
+    FrameTimeline(std::shared_ptr<TimeStats> timeStats);
     ~FrameTimeline() = default;
 
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
     std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
-            const std::string& layerName, std::optional<int64_t> token) override;
+            uid_t ownerUid, std::string layerName, std::string debugName,
+            std::optional<int64_t> token) override;
     void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
                          SurfaceFrame::PresentState state) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
@@ -278,7 +267,7 @@
         std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
 
         PredictionState predictionState;
-        JankType jankType = JankType::None; // Enum for the type of jank
+        TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank
         int32_t jankMetadata = 0x0;         // Additional details about the jank
     };
 
@@ -300,6 +289,7 @@
     TokenManager mTokenManager;
     std::mutex mMutex;
     uint32_t mMaxDisplayFrames;
+    std::shared_ptr<TimeStats> mTimeStats;
     static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
     // The initial container size for the vector<SurfaceFrames> inside display frame. Although this
     // number doesn't represent any bounds on the number of surface frames that can go in a display
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f657a00..ffae56f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -482,6 +482,7 @@
     compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
     compositionState->alpha = alpha;
     compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+    compositionState->blurRegions = drawingState.blurRegions;
 }
 
 void Layer::prepareGeometryCompositionState() {
@@ -550,7 +551,8 @@
             isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
 
     // Force client composition for special cases known only to the front-end.
-    if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+    if (isHdrY410() || usesRoundedCorners || drawShadows() ||
+        getDrawingState().blurRegions.size() > 0) {
         compositionState->forceClientComposition = true;
     }
 }
@@ -646,6 +648,7 @@
     layerSettings.alpha = alpha;
     layerSettings.sourceDataspace = getDataSpace();
     layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+    layerSettings.blurRegions = getBlurRegions();
     return layerSettings;
 }
 
@@ -897,7 +900,8 @@
                 : std::make_optional(stateToCommit->frameTimelineVsyncId);
 
         auto surfaceFrame =
-                mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId);
+                mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerUid(), mName,
+                                                                     mTransactionName, vsyncId);
         surfaceFrame->setActualQueueTime(stateToCommit->postTime);
         // For transactions we set the acquire fence time to the post time as we
         // don't have a buffer. For BufferStateLayer it is overridden in
@@ -1285,6 +1289,14 @@
     return true;
 }
 
+bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
+    mCurrentState.sequence++;
+    mCurrentState.blurRegions = blurRegions;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setFlags(uint8_t flags, uint8_t mask) {
     const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
     if (mCurrentState.flags == newFlags) return false;
@@ -1728,7 +1740,7 @@
                                      FrameEventHistoryDelta* outDelta) {
     if (newTimestamps) {
         mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
-                                          getName().c_str(), newTimestamps->postedTime);
+                                          getName().c_str(), mOwnerUid, newTimestamps->postedTime);
         mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
                                               newTimestamps->acquireFence);
     }
@@ -2172,6 +2184,10 @@
     return getDrawingState().backgroundBlurRadius;
 }
 
+const std::vector<BlurRegion>& Layer::getBlurRegions() const {
+    return getDrawingState().blurRegions;
+}
+
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7112cbc..5cb56a5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -26,6 +26,7 @@
 #include <renderengine/Mesh.h>
 #include <renderengine/Texture.h>
 #include <sys/types.h>
+#include <ui/BlurRegion.h>
 #include <ui/FloatRect.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
@@ -255,6 +256,9 @@
         // be rendered around the layer.
         float shadowRadius;
 
+        // Layer regions that are made of custom materials, like frosted glass
+        std::vector<BlurRegion> blurRegions;
+
         // Priority of the layer assigned by Window Manager.
         int32_t frameRateSelectionPriority;
 
@@ -391,6 +395,7 @@
     // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
     // is specified in pixels.
     virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
+    virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setFlags(uint8_t flags, uint8_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
@@ -1038,6 +1043,10 @@
     // Can only be accessed with the SF state lock held.
     std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
 
+    // The owner of the layer. If created from a non system process, it will be the calling uid.
+    // If created from a system process, the value can be passed in.
+    uid_t mOwnerUid;
+
 private:
     virtual void setTransformHint(ui::Transform::RotationFlags) {}
 
@@ -1096,10 +1105,6 @@
     pid_t mCallingPid;
     uid_t mCallingUid;
 
-    // The owner of the layer. If created from a non system process, it will be the calling uid.
-    // If created from a system process, the value can be passed in.
-    uid_t mOwnerUid;
-
     // The current layer is a clone of mClonedFrom. This means that this layer will update it's
     // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
     // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1110,6 +1115,9 @@
     // final shadow radius for this layer. If a shadow is specified for a layer, then effective
     // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
     float mEffectiveShadowRadius = 0.f;
+
+    // A list of regions on this layer that should have blurs.
+    const std::vector<BlurRegion>& getBlurRegions() const;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3977e2b..fde38c9 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,7 @@
         mInterceptor(mFactory.createSurfaceInterceptor()),
         mTimeStats(std::make_shared<impl::TimeStats>()),
         mFrameTracer(std::make_unique<FrameTracer>()),
-        mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()),
+        mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -3684,6 +3684,9 @@
     if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
         if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eBlurRegionsChanged) {
+        if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 2d5566f..3548923 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -140,6 +140,7 @@
     addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
     addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
+    addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
     if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
         addDeferTransactionLocked(transaction, layerId,
                                   layer->mCurrentState.barrierLayer_legacy.promote(),
@@ -361,6 +362,25 @@
     blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
 }
 
+void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+                                              const std::vector<BlurRegion>& blurRegions) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
+    for (const auto blurRegion : blurRegions) {
+        const auto blurRegionChange = blurRegionsChange->add_blur_regions();
+        blurRegionChange->set_blur_radius(blurRegion.blurRadius);
+        blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
+        blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
+        blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
+        blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
+        blurRegionChange->set_alpha(blurRegion.alpha);
+        blurRegionChange->set_left(blurRegion.left);
+        blurRegionChange->set_top(blurRegion.top);
+        blurRegionChange->set_right(blurRegion.right);
+        blurRegionChange->set_bottom(blurRegion.bottom);
+    }
+}
+
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
         const sp<const Layer>& layer, uint64_t frameNumber)
 {
@@ -456,6 +476,9 @@
     if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
         addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
     }
+    if (state.what & layer_state_t::eBlurRegionsChanged) {
+        addBlurRegionsLocked(transaction, layerId, state.blurRegions);
+    }
     if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
         if (state.barrierSurfaceControl_legacy != nullptr) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 97ff547..3df79c6 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -165,6 +165,8 @@
     void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
     void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
                                        int32_t backgroundBlurRadius);
+    void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+                              const std::vector<BlurRegion>& effectRegions);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
     void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 37194c6..fe9e737 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -33,6 +30,8 @@
 #include <algorithm>
 #include <chrono>
 
+#include "timestatsproto/TimeStatsHelper.h"
+
 namespace android {
 
 namespace impl {
@@ -115,6 +114,13 @@
                                        mMaxPulledHistogramBuckets);
     mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
                                              renderEngineTimingBytes.size());
+
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed);
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed);
     mStatsDelegate->statsEventBuild(event);
     clearGlobalLocked();
 
@@ -160,6 +166,13 @@
 
         mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
         mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->uid);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
+        mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
 
         mStatsDelegate->statsEventBuild(event);
     }
@@ -397,11 +410,13 @@
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
         if (prevTimeRecord.ready) {
+            uid_t uid = layerRecord.uid;
             const std::string& layerName = layerRecord.layerName;
-            if (!mTimeStats.stats.count(layerName)) {
-                mTimeStats.stats[layerName].layerName = layerName;
+            if (!mTimeStats.stats.count({uid, layerName})) {
+                mTimeStats.stats[{uid, layerName}].uid = uid;
+                mTimeStats.stats[{uid, layerName}].layerName = layerName;
             }
-            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
+            TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
             timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -462,8 +477,13 @@
             layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
 }
 
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+    return mTimeStats.stats.count({uid, layerName}) > 0 ||
+            mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+}
+
 void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                            nsecs_t postTime) {
+                            uid_t uid, nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
@@ -471,11 +491,12 @@
           postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+    if (!canAddNewAggregatedStats(uid, layerName)) {
         return;
     }
     if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
         layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerId].uid = uid;
         mTimeStatsTracker[layerId].layerName = layerName;
     }
     if (!mTimeStatsTracker.count(layerId)) return;
@@ -655,6 +676,66 @@
     flushAvailableRecordsToStatsLocked(layerId);
 }
 
+template <class T>
+static void updateJankPayload(T& t, int32_t reasons) {
+    t.jankPayload.totalFrames++;
+
+    static const constexpr int32_t kValidJankyReason =
+            TimeStats::JankType::SurfaceFlingerDeadlineMissed |
+            TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed |
+            TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display;
+    if (reasons & kValidJankyReason) {
+        t.jankPayload.totalJankyFrames++;
+        if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) {
+            t.jankPayload.totalSFLongCpu++;
+        }
+        if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
+            t.jankPayload.totalSFLongGpu++;
+        }
+        if ((reasons & TimeStats::JankType::Display) != 0) {
+            t.jankPayload.totalSFUnattributed++;
+        }
+        if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) {
+            t.jankPayload.totalAppUnattributed++;
+        }
+    }
+}
+
+void TimeStats::incrementJankyFrames(int32_t reasons) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons);
+}
+
+void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    // Only update layer stats if we're allowed to do so.
+    // As an implementation detail, we do this because this method is expected to be
+    // called from FrameTimeline, which is allowed to do jank analysis well after a frame is
+    // presented. This means that we can't rely on TimeStats to flush layer records over to the
+    // aggregated stats.
+    if (!canAddNewAggregatedStats(uid, layerName)) {
+        return;
+    }
+
+    // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences
+    // before TimeStats does.
+    if (!mTimeStats.stats.count({uid, layerName})) {
+        mTimeStats.stats[{uid, layerName}].uid = uid;
+        mTimeStats.stats[{uid, layerName}].layerName = layerName;
+    }
+
+    TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+    updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+}
+
 void TimeStats::onDestroy(int32_t layerId) {
     ATRACE_CALL();
     ALOGV("[%d]-onDestroy", layerId);
@@ -860,6 +941,7 @@
     mTimeStats.presentToPresent.hist.clear();
     mTimeStats.frameDuration.hist.clear();
     mTimeStats.renderEngineTiming.hist.clear();
+    mTimeStats.jankPayload = TimeStatsHelper::JankPayload();
     mTimeStats.refreshRateStats.clear();
     mPowerTime.prevTime = systemTime();
     mGlobalRecord.prevPresentTime = 0;
@@ -905,6 +987,3 @@
 } // namespace impl
 
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8de5d0c..4fa0a02 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,7 @@
 #pragma once
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <cstdint>
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
@@ -85,7 +86,7 @@
                                             const std::shared_ptr<FenceTime>& readyFence) = 0;
 
     virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
-                             nsecs_t postTime) = 0;
+                             uid_t uid, nsecs_t postTime) = 0;
     virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
     // Reasons why latching a particular buffer may be skipped
     enum class LatchSkipReason {
@@ -108,6 +109,40 @@
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+    // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry.
+    enum JankType {
+        // No Jank
+        None = 0x0,
+        // Jank not related to SurfaceFlinger or the App
+        Display = 0x1,
+        // SF took too long on the CPU
+        SurfaceFlingerDeadlineMissed = 0x2,
+        // SF took too long on the GPU
+        SurfaceFlingerGpuDeadlineMissed = 0x4,
+        // Either App or GPU took too long on the frame
+        AppDeadlineMissed = 0x8,
+        // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
+        // jank
+        // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+        PredictionExpired = 0x10,
+        // Latching a buffer early might cause an early present of the frame
+        SurfaceFlingerEarlyLatch = 0x20,
+    };
+
+    // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure
+    // responsible for computing jank in the system, this is expected to be called from
+    // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no
+    // jank reasons, then total frames are incremented but jank is not, for accurate accounting of
+    // janky frames.
+    virtual void incrementJankyFrames(int32_t reasons) = 0;
+    // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as
+    // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible
+    // for computing jank in the system, this is expected to be called from FrameTimeline, rather
+    // than directly from SurfaceFlinger or individual layers.
+    // If there are no jank reasons, then total frames are incremented but jank is not, for accurate
+    // accounting of janky frames.
+    virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0;
     // Clean up the layer record
     virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -142,6 +177,7 @@
     };
 
     struct LayerRecord {
+        uid_t uid;
         std::string layerName;
         // This is the index in timeRecords, at which the timestamps for that
         // specific frame are still not fully received. This is not waiting for
@@ -241,7 +277,7 @@
     void recordRenderEngineDuration(nsecs_t startTime,
                                     const std::shared_ptr<FenceTime>& readyFence) override;
 
-    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
+    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
                      nsecs_t postTime) override;
     void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
     void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
@@ -253,6 +289,8 @@
     void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
     void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence) override;
+    void incrementJankyFrames(int32_t reasons) override;
+    void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override;
     // Clean up the layer record
     void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -276,6 +314,7 @@
     void flushAvailableRecordsToStatsLocked(int32_t layerId);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
+    bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
 
     void enable();
     void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d77387a..0fb748f 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -77,14 +77,28 @@
     return result;
 }
 
+std::string TimeStatsHelper::JankPayload::toString() const {
+    std::string result;
+    StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames);
+    StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames);
+    StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu);
+    StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu);
+    StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed);
+    StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed);
+    return result;
+}
+
 std::string TimeStatsHelper::TimeStatsLayer::toString() const {
     std::string result = "\n";
+    StringAppendF(&result, "uid = %d\n", uid);
     StringAppendF(&result, "layerName = %s\n", layerName.c_str());
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
     StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
     StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
+    result.append("Jank payload for this layer:\n");
+    result.append(jankPayload.toString());
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         const float averageTime = iter->second.averageTime();
@@ -110,6 +124,8 @@
     StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
     StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
     StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+    result.append("Global aggregated jank payload:\n");
+    result.append(jankPayload.toString());
     StringAppendF(&result, "displayConfigStats is as below:\n");
     for (const auto& [fps, duration] : refreshRateStats) {
         StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0c75f96..033eb5d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -40,14 +40,28 @@
         std::string toString() const;
     };
 
+    struct JankPayload {
+        // note that transactions are counted for these frames.
+        int32_t totalFrames = 0;
+        int32_t totalJankyFrames = 0;
+        int32_t totalSFLongCpu = 0;
+        int32_t totalSFLongGpu = 0;
+        int32_t totalSFUnattributed = 0;
+        int32_t totalAppUnattributed = 0;
+
+        std::string toString() const;
+    };
+
     class TimeStatsLayer {
     public:
+        uid_t uid;
         std::string layerName;
         std::string packageName;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
         int32_t lateAcquireFrames = 0;
         int32_t badDesiredPresentFrames = 0;
+        JankPayload jankPayload;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
@@ -69,8 +83,17 @@
         Histogram presentToPresent;
         Histogram frameDuration;
         Histogram renderEngineTiming;
-        std::unordered_map<std::string, TimeStatsLayer> stats;
+
+        struct StatsHasher {
+            size_t operator()(const std::pair<uid_t, std::string>& p) const {
+                // Normally this isn't a very good hash function due to symmetry reasons,
+                // but these are distinct types so this should be good enough
+                return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+            }
+        };
+        std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats;
         std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+        JankPayload jankPayload;
 
         std::string toString(std::optional<uint32_t> maxLayers) const;
         SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index f3f5626..9f25674 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -125,6 +125,9 @@
   int32 background_blur_radius = 52;
 
   uint32 owner_uid = 53;
+
+  // Regions of a layer, where blur should be applied.
+  repeated BlurRegion blur_regions = 54;
 }
 
 message PositionProto {
@@ -211,3 +214,16 @@
   // This will be a 4x4 matrix of float values
   repeated float val = 1;
 }
+
+message BlurRegion {
+    uint32 blur_radius = 1;
+    uint32 corner_radius_tl = 2;
+    uint32 corner_radius_tr = 3;
+    uint32 corner_radius_bl = 4;
+    float corner_radius_br = 5;
+    float alpha = 6;
+    int32 left = 7;
+    int32 top = 8;
+    int32 right = 9;
+    int32 bottom = 10;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a5a569b..81e648a 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -51,6 +51,7 @@
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
 const float SHADOW_RADIUS_UPDATE = 35.0f;
+std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
 
 const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
 constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -181,6 +182,7 @@
     bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
     bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
                                          bool foundBackgroundBlurRadius);
+    bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -219,6 +221,7 @@
     void cropUpdate(Transaction&);
     void cornerRadiusUpdate(Transaction&);
     void backgroundBlurRadiusUpdate(Transaction&);
+    void blurRegionsUpdate(Transaction&);
     void matrixUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
     void layerStackUpdate(Transaction&);
@@ -357,6 +360,12 @@
     t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
 }
 
+void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
+    BLUR_REGIONS_UPDATE.empty();
+    BLUR_REGIONS_UPDATE.push_back(BlurRegion());
+    t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
@@ -430,6 +439,7 @@
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
     runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
+    runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -518,6 +528,17 @@
     return foundBackgroundBlur;
 }
 
+bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
+                                                    bool foundBlurRegions) {
+    bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
+    if (hasBlurRegions && !foundBlurRegions) {
+        foundBlurRegions = true;
+    } else if (hasBlurRegions && foundBlurRegions) {
+        []() { FAIL(); }();
+    }
+    return foundBlurRegions;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
@@ -706,6 +727,9 @@
                         case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
                             foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+                            foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
                             break;
@@ -889,6 +913,11 @@
                 SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
+                SurfaceChange::SurfaceChangeCase::kBlurRegions);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
     captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
 }
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2861013..8d094e4 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -8,6 +8,7 @@
          "FakeComposerUtils.cpp",
          "SFFakeHwc_test.cpp"
     ],
+    require_root: true,
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 69efd7f..5e9d64a 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "gmock/gmock-spec-builders.h"
+#include "mock/MockTimeStats.h"
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -23,6 +25,11 @@
 #include <cinttypes>
 
 using namespace std::chrono_literals;
+using testing::Contains;
+
+MATCHER_P(HasBit, bit, "") {
+    return (arg & bit) != 0;
+}
 
 namespace android::frametimeline {
 
@@ -41,7 +48,8 @@
     }
 
     void SetUp() override {
-        mFrameTimeline = std::make_unique<impl::FrameTimeline>();
+        mTimeStats = std::make_shared<mock::TimeStats>();
+        mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
         mTokenManager = &mFrameTimeline->mTokenManager;
         maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
         maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
@@ -76,6 +84,7 @@
         return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
     }
 
+    std::shared_ptr<mock::TimeStats> mTimeStats;
     std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
     impl::TokenManager* mTokenManager;
     FenceToFenceTimeMap fenceFactory;
@@ -83,6 +92,10 @@
     nsecs_t maxTokenRetentionTime;
 };
 
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+static constexpr const uid_t sUidOne = 0;
+
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     EXPECT_EQ(getPredictions().size(), 1);
@@ -99,21 +112,24 @@
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, std::nullopt);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
     flushTokens(systemTime() + maxTokenRetentionTime);
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, token1);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                   sLayerNameOne, token1);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -125,7 +141,8 @@
 
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                    sLayerNameOne, token1);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20);
@@ -148,8 +165,12 @@
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken1);
-    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", surfaceFrameToken1);
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken1);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameTwo, sLayerNameTwo,
+                                                       surfaceFrameToken1);
     mFrameTimeline->setSfWakeUp(sfToken1, 22);
     mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
                                     SurfaceFrame::PresentState::Presented);
@@ -168,7 +189,9 @@
 
     // Trigger a flush by finalizing the next DisplayFrame
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    auto surfaceFrame3 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken2);
+    auto surfaceFrame3 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken2);
     mFrameTimeline->setSfWakeUp(sfToken2, 52);
     mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
     mFrameTimeline->setSfPresent(56, presentFence2);
@@ -189,7 +212,9 @@
                 {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
         int64_t sfToken = mTokenManager->generateTokenForPredictions(
                 {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
-        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+        auto surfaceFrame =
+                mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                           surfaceFrameToken);
         mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
         mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
                                         SurfaceFrame::PresentState::Presented);
@@ -209,7 +234,9 @@
             {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
     int64_t sfToken = mTokenManager->generateTokenForPredictions(
             {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken);
     mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
     mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
     mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
@@ -224,7 +251,8 @@
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+            mFrameTimeline->createSurfaceFrameForToken(0, "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", std::nullopt);
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -232,7 +260,8 @@
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
     auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+            mFrameTimeline->createSurfaceFrameForToken(0, "acquireFenceAfterQueue",
+                                                       "acquireFenceAfterQueue", std::nullopt);
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -244,7 +273,8 @@
 
     // Size shouldn't exceed maxDisplayFrames - 64
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
-        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                       sLayerNameOne, std::nullopt);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22);
         mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -258,7 +288,8 @@
     EXPECT_EQ(*maxDisplayFrames, 256);
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
-        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                       sLayerNameOne, std::nullopt);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22);
         mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -272,7 +303,8 @@
     EXPECT_EQ(*maxDisplayFrames, 128);
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
-        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+        auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne,
+                                                                       sLayerNameOne, std::nullopt);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22);
         mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -281,4 +313,90 @@
     }
     EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
 }
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne,
+                                     HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken1);
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+                                 presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken1);
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+                                 presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+    EXPECT_CALL(*mTimeStats,
+                incrementJankyFrames(sUidOne, sLayerNameOne,
+                                     HasBit(TimeStats::JankType::AppDeadlineMissed)));
+    EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed)));
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+            {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+             std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(sUidOne, sLayerNameOne, sLayerNameOne,
+                                                       surfaceFrameToken1);
+    surfaceFrame1->setAcquireFenceTime(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
+    mFrameTimeline->setSfWakeUp(sfToken1,
+                                std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+
+    mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+                                    SurfaceFrame::PresentState::Presented);
+    presentFence1->signalForTest(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+    mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+                                 presentFence1);
+}
+
 } // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 0a24650..a90f424 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -58,6 +58,7 @@
 #define FMT_STRING         false
 #define LAYER_ID_0         0
 #define LAYER_ID_1         1
+#define UID_0              123
 #define LAYER_ID_INVALID   -1
 #define NUM_LAYERS         1
 #define NUM_LAYERS_INVALID "INVALID"
@@ -227,7 +228,8 @@
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
     switch (type) {
         case TimeStamp::POST:
-            ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+            ASSERT_NO_FATAL_FAILURE(
+                    mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
             break;
         case TimeStamp::ACQUIRE:
             ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -349,6 +351,61 @@
     EXPECT_THAT(result, HasSubstr(expectedResult));
 }
 
+TEST_F(TimeStatsTest, canIncreaseJankyFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(4);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) {
+    // this stat is not in the proto so verify by checking the string dump
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "jankyFrames = " + std::to_string(4);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+    expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
 TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
     // this stat is not in the proto so verify by checking the string dump
     constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -789,6 +846,16 @@
                                                    .count());
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
             std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
     EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
 
     const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
@@ -797,6 +864,11 @@
     EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
     EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
     EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+    EXPECT_THAT(result, HasSubstr("jankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0"));
+    EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0"));
 }
 
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -904,6 +976,8 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
     mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
     mTimeStats->setPowerMode(PowerMode::ON);
     mTimeStats->recordFrameDuration(1000000, 3000000);
@@ -913,6 +987,12 @@
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
 
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
                                      android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -944,6 +1024,12 @@
                                                              expectedRenderEngineTiming.c_str(),
                                                      expectedRenderEngineTiming.size()),
                                              expectedRenderEngineTiming.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
@@ -975,6 +1061,15 @@
     }
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+                                     TimeStats::JankType::AppDeadlineMissed);
+    mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
     EXPECT_THAT(mDelegate->mAtomTags,
                 UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
                                      android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -1033,6 +1128,14 @@
         EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
         EXPECT_CALL(*mDelegate,
                     statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ff37ec8..99ec353 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,7 +41,7 @@
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
     MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
     MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
@@ -50,6 +50,8 @@
     MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD1(incrementJankyFrames, void(int32_t));
+    MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
     MOCK_METHOD1(setPowerMode,