Merge "StorageManager: remove old isFileEncrypted* methods" into main
diff --git a/Android.bp b/Android.bp
index 1aa297f..b072087 100644
--- a/Android.bp
+++ b/Android.bp
@@ -136,6 +136,9 @@
         // For the generated R.java and Manifest.java
         ":framework-res{.aapt.srcjar}",
 
+        // Java/AIDL sources to be moved out to CrashRecovery module
+        ":framework-crashrecovery-sources",
+
         // etc.
         ":framework-javastream-protos",
         ":statslog-framework-java-gen", // FrameworkStatsLog.java
diff --git a/core/api/current.txt b/core/api/current.txt
index 958b2f9..9bcbd72 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33236,7 +33236,7 @@
     method public static android.os.Message obtain(android.os.Handler, int, Object);
     method public static android.os.Message obtain(android.os.Handler, int, int, int);
     method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
-    method public android.os.Bundle peekData();
+    method @Nullable public android.os.Bundle peekData();
     method public void recycle();
     method public void sendToTarget();
     method public void setAsynchronous(boolean);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3d01011..95dc07f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -298,7 +298,7 @@
     field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
     field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
     field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
-    field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
+    field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
     field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
     field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
     field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 72fb4ae..cc18482 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -436,6 +437,7 @@
      * @see #getData()
      * @see #setData(Bundle)
      */
+    @Nullable
     public Bundle peekData() {
         return data;
     }
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index cc54266..5c4aa4a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -52,8 +52,20 @@
       ],
       "name": "FrameworksServicesTests",
       "options": [
-        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
-        { "include-filter": "com.android.server.power.stats.BatteryStatsTests" }
+        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
+      ]
+    },
+    {
+      "file_patterns": [
+        "BatteryStats[^/]*\\.java",
+        "BatteryUsageStats[^/]*\\.java",
+        "PowerComponents\\.java",
+        "[^/]*BatteryConsumer[^/]*\\.java"
+      ],
+      "name": "FrameworksServicesTests",
+      "options": [
+        { "include-filter": "com.android.server.power.stats" },
+        { "exclude-filter": "com.android.server.power.stats.BatteryStatsTests" }
       ]
     },
     {
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index e5b76f6..bf22dcc 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -4,14 +4,16 @@
 
 # Android Storage Team
 alukin@google.com
-corinac@google.com
+ankitavyas@google.com
 dipankarb@google.com
+gargshivam@google.com
 krishang@google.com
+riyaghai@google.com
 sahanas@google.com
 sergeynv@google.com
+shikhamalhotra@google.com
 shubhisaxena@google.com
 tylersaunders@google.com
 
 maco@google.com
 nandana@google.com
-narayan@google.com
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
index bb0e6ab..cb0b5fa 100644
--- a/core/java/android/service/notification/OWNERS
+++ b/core/java/android/service/notification/OWNERS
@@ -2,6 +2,7 @@
 
 juliacr@google.com
 yurilin@google.com
+matiashe@google.com
 jeffdq@google.com
 dsandler@android.com
 dsandler@google.com
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 03a6a00..73d18f9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22444,6 +22444,22 @@
     }
 
     /**
+     * Configure the {@link android.graphics.RenderEffect} to apply to the backdrop contents of this
+     * View. This will apply a visual effect to the result of the backdrop contents of this View
+     * before it is drawn. For example if
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+     * is provided, the previous content behind this View will be blurred before this View is drawn.
+     * @param renderEffect to be applied to the View. Passing null clears the previously configured
+     *                     {@link RenderEffect}
+     * @hide
+     */
+    public void setBackdropRenderEffect(@Nullable RenderEffect renderEffect) {
+        if (mRenderNode.setBackdropRenderEffect(renderEffect)) {
+            invalidateViewProperty(true, true);
+        }
+    }
+
+    /**
      * Updates the {@link Paint} object used with the current layer (used only if the current
      * layer type is not set to {@link #LAYER_TYPE_NONE}). Changed properties of the Paint
      * provided to {@link #setLayerType(int, android.graphics.Paint)} will be used the next time
diff --git a/core/java/com/android/internal/pm/OWNERS b/core/java/com/android/internal/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/internal/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/core/java/com/android/server/pm/OWNERS b/core/java/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/server/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cc2d2e2..05117ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -457,9 +457,4 @@
             ],
         },
     },
-
-    // Workaround Clang LTO crash.
-    lto: {
-        never: true,
-    },
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0e753e5..f7b6a7b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2278,6 +2278,7 @@
     <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
         <p>Not for use by third-party or privileged applications.
         @SystemApi
+        @FlaggedApi("com.android.net.flags.register_nsd_offload_engine")
         @hide This should only be used by system apps.
     -->
     <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 2e91c24..15d26eb 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -971,6 +971,23 @@
     }
 
     /**
+     * Configure the {@link android.graphics.RenderEffect} to apply to the backdrop contents of
+     * this RenderNode. This will apply a visual effect to the result of the backdrop contents
+     * of this RenderNode before the RenderNode is drawn into the destination. For example if
+     * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+     * is provided, the previous content behind this RenderNode will be blurred before the
+     * RenderNode is drawn in to the destination.
+     * @param renderEffect to be applied to the backdrop contents of this RenderNode. Passing
+     *          null clears all previously configured RenderEffects
+     * @return True if the value changed, false if the new value was the same as the previous value.
+     * @hide
+     */
+    public boolean setBackdropRenderEffect(@Nullable RenderEffect renderEffect) {
+        return nSetBackdropRenderEffect(mNativeRenderNode,
+                renderEffect != null ? renderEffect.getNativeInstance() : 0);
+    }
+
+    /**
      * Returns the translucency level of this display list.
      *
      * @return A value between 0.0f and 1.0f
@@ -1797,6 +1814,9 @@
     private static native boolean nSetRenderEffect(long renderNode, long renderEffect);
 
     @CriticalNative
+    private static native boolean nSetBackdropRenderEffect(long renderNode, long renderEffect);
+
+    @CriticalNative
     private static native boolean nSetHasOverlappingRendering(long renderNode,
             boolean hasOverlappingRendering);
 
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bce..dd703f5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import java.util.Calendar;
-import java.util.Objects;
 
 /**
  * @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@
         return new KeyStore2();
     }
 
+    /**
+     * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+     *
+     * @throws IllegalStateException if the KeystoreService is not available or has not
+     * been initialized when called. This is a state that should not happen and indicates
+     * and error somewhere in the stack or with the calling processes access permissions.
+     */
     @NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
         if (mBinder == null || retryLookup) {
             mBinder = IKeystoreService.Stub.asInterface(ServiceManager
-                    .getService(KEYSTORE2_SERVICE_NAME));
-            Binder.allowBlocking(mBinder.asBinder());
+                .getService(KEYSTORE2_SERVICE_NAME));
         }
-        return Objects.requireNonNull(mBinder);
+        if (mBinder == null) {
+            throw new IllegalStateException(
+                    "Could not connect to Keystore service. Keystore may have crashed or not been"
+                            + " initialized");
+        }
+        Binder.allowBlocking(mBinder.asBinder());
+        return mBinder;
     }
 
     void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index db58147..b5e6f94 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -514,6 +514,7 @@
         "canvas/CanvasOpRasterizer.cpp",
         "effects/StretchEffect.cpp",
         "effects/GainmapRenderer.cpp",
+        "pipeline/skia/BackdropFilterDrawable.cpp",
         "pipeline/skia/HolePunch.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
         "pipeline/skia/SkiaRecordingCanvas.cpp",
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 1c39db3..1dd22cf 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -260,6 +260,12 @@
         pushStagingDisplayListChanges(observer, info);
     }
 
+    // always damageSelf when filtering backdrop content, or else the BackdropFilterDrawable will
+    // get a wrong snapshot of previous content.
+    if (mProperties.layerProperties().getBackdropImageFilter()) {
+        damageSelf(info);
+    }
+
     if (mDisplayList) {
         info.out.hasFunctors |= mDisplayList.hasFunctor();
         mHasHolePunches = mDisplayList.hasHolePunches();
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 0589f13..c537123 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -55,6 +55,12 @@
     return true;
 }
 
+bool LayerProperties::setBackdropImageFilter(SkImageFilter* imageFilter) {
+    if (mBackdropImageFilter.get() == imageFilter) return false;
+    mBackdropImageFilter = sk_ref_sp(imageFilter);
+    return true;
+}
+
 bool LayerProperties::setFromPaint(const SkPaint* paint) {
     bool changed = false;
     changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -70,6 +76,7 @@
     setXferMode(other.xferMode());
     setColorFilter(other.getColorFilter());
     setImageFilter(other.getImageFilter());
+    setBackdropImageFilter(other.getBackdropImageFilter());
     mStretchEffect = other.mStretchEffect;
     return *this;
 }
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 064ba7a..e358b57 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -97,8 +97,12 @@
 
     bool setImageFilter(SkImageFilter* imageFilter);
 
+    bool setBackdropImageFilter(SkImageFilter* imageFilter);
+
     SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
 
+    SkImageFilter* getBackdropImageFilter() const { return mBackdropImageFilter.get(); }
+
     const StretchEffect& getStretchEffect() const { return mStretchEffect; }
 
     StretchEffect& mutableStretchEffect() { return mStretchEffect; }
@@ -129,6 +133,7 @@
     SkBlendMode mMode;
     sk_sp<SkColorFilter> mColorFilter;
     sk_sp<SkImageFilter> mImageFilter;
+    sk_sp<SkImageFilter> mBackdropImageFilter;
     StretchEffect mStretchEffect;
 };
 
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8c7b9a4..2a218a2 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -243,6 +243,13 @@
     return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
 }
 
+static jboolean android_view_RenderNode_setBackdropRenderEffect(
+        CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong renderEffectPtr) {
+    SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+    return SET_AND_DIRTY(mutateLayerProperties().setBackdropImageFilter, imageFilter,
+                         RenderNode::GENERIC);
+}
+
 static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
         bool hasOverlappingRendering) {
     return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -792,6 +799,8 @@
 
         {"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
         {"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
+        {"nSetBackdropRenderEffect", "(JJ)Z",
+         (void*)android_view_RenderNode_setBackdropRenderEffect},
         {"nSetHasOverlappingRendering", "(JZ)Z",
          (void*)android_view_RenderNode_setHasOverlappingRendering},
         {"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
new file mode 100644
index 0000000..ffad699
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "BackdropFilterDrawable.h"
+
+#include <SkImage.h>
+#include <SkSurface.h>
+
+#include "RenderNode.h"
+#include "RenderNodeDrawable.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+BackdropFilterDrawable::~BackdropFilterDrawable() {}
+
+bool BackdropFilterDrawable::prepareToDraw(SkCanvas* canvas, const RenderProperties& properties,
+                                           int backdropImageWidth, int backdropImageHeight) {
+    // the drawing bounds for blurred content.
+    mDstBounds.setWH(properties.getWidth(), properties.getHeight());
+
+    float alphaMultiplier = 1.0f;
+    RenderNodeDrawable::setViewProperties(properties, canvas, &alphaMultiplier, true);
+
+    // get proper subset for previous content.
+    canvas->getTotalMatrix().mapRect(&mImageSubset, mDstBounds);
+    SkRect imageSubset(mImageSubset);
+    // ensure the subset is inside bounds of previous content.
+    if (!mImageSubset.intersect(SkRect::MakeWH(backdropImageWidth, backdropImageHeight))) {
+        return false;
+    }
+
+    // correct the drawing bounds if subset was changed.
+    if (mImageSubset != imageSubset) {
+        SkMatrix inverse;
+        if (canvas->getTotalMatrix().invert(&inverse)) {
+            inverse.mapRect(&mDstBounds, mImageSubset);
+        }
+    }
+
+    // follow the alpha from the target RenderNode.
+    mPaint.setAlpha(properties.layerProperties().alpha() * alphaMultiplier);
+    return true;
+}
+
+void BackdropFilterDrawable::onDraw(SkCanvas* canvas) {
+    const RenderProperties& properties = mTargetRenderNode->properties();
+    auto* backdropFilter = properties.layerProperties().getBackdropImageFilter();
+    auto* surface = canvas->getSurface();
+    if (!backdropFilter || !surface) {
+        return;
+    }
+
+    auto backdropImage = surface->makeImageSnapshot();
+    // sync necessary properties from target RenderNode.
+    if (!prepareToDraw(canvas, properties, backdropImage->width(), backdropImage->height())) {
+        return;
+    }
+
+    auto imageSubset = mImageSubset.roundOut();
+    backdropImage =
+            backdropImage->makeWithFilter(canvas->recordingContext(), backdropFilter, imageSubset,
+                                          imageSubset, &mOutSubset, &mOutOffset);
+    canvas->drawImageRect(backdropImage, SkRect::Make(mOutSubset), mDstBounds,
+                          SkSamplingOptions(SkFilterMode::kLinear), &mPaint,
+                          SkCanvas::kStrict_SrcRectConstraint);
+}
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.h b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
new file mode 100644
index 0000000..9e35837
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable captures it's backdrop content and render it with a
+ * image filter.
+ */
+class BackdropFilterDrawable : public SkDrawable {
+public:
+    BackdropFilterDrawable(RenderNode* renderNode, SkCanvas* canvas)
+            : mTargetRenderNode(renderNode), mBounds(canvas->getLocalClipBounds()) {}
+
+    ~BackdropFilterDrawable();
+
+private:
+    RenderNode* mTargetRenderNode;
+    SkPaint mPaint;
+
+    SkRect mDstBounds;
+    SkRect mImageSubset;
+    SkIRect mOutSubset;
+    SkIPoint mOutOffset;
+
+    /**
+     * Check all necessary properties before actual drawing.
+     * Return true if ready to draw.
+     */
+    bool prepareToDraw(SkCanvas* canvas, const RenderProperties& properties, int backdropImageWidth,
+                       int backdropImageHeight);
+
+protected:
+    void onDraw(SkCanvas* canvas) override;
+
+    virtual SkRect onGetBounds() override { return mBounds; }
+    const SkRect mBounds;
+};
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index da4f66d..9d72c23 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -362,7 +362,7 @@
 }
 
 void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
-                                           float* alphaMultiplier) {
+                                           float* alphaMultiplier, bool ignoreLayer) {
     if (properties.getLeft() != 0 || properties.getTop() != 0) {
         canvas->translate(properties.getLeft(), properties.getTop());
     }
@@ -378,7 +378,8 @@
             canvas->concat(*properties.getTransformMatrix());
         }
     }
-    if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
+    if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale &&
+        !ignoreLayer) {
         const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
         if (!stretch.isEmpty()) {
             canvas->concat(
@@ -388,10 +389,10 @@
     const bool isLayer = properties.effectiveLayerType() != LayerType::None;
     int clipFlags = properties.getClippingFlags();
     if (properties.getAlpha() < 1) {
-        if (isLayer) {
+        if (isLayer && !ignoreLayer) {
             clipFlags &= ~CLIP_TO_BOUNDS;  // bounds clipping done by layer
         }
-        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+        if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering()) || ignoreLayer) {
             *alphaMultiplier = properties.getAlpha();
         } else {
             // savelayer needed to create an offscreen buffer
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index c7582e7..818ac45 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -120,7 +120,7 @@
      * Applies the rendering properties of a view onto a SkCanvas.
      */
     static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
-                                  float* alphaMultiplier);
+                                  float* alphaMultiplier, bool ignoreLayer = false);
 
     /**
      * Stores transform on the canvas at time of recording and is used for
@@ -149,6 +149,11 @@
      * display list that is searched for any render nodes with getProjectBackwards==true
      */
     SkiaDisplayList* mProjectedDisplayList = nullptr;
+
+    /**
+     * Allow BackdropFilterDrawable to apply same render properties onto SkCanvas.
+     */
+    friend class BackdropFilterDrawable;
 };
 
 }  // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3ca7eeb..58c14c1 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -37,6 +37,7 @@
 #include "NinePatchUtils.h"
 #include "RenderNode.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
 #ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
 #include "pipeline/skia/GLFunctorDrawable.h"
 #include "pipeline/skia/VkFunctorDrawable.h"
@@ -168,6 +169,14 @@
         // Put Vulkan WebViews with non-rectangular clips in a HW layer
         renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
     }
+
+    // draw backdrop filter drawable if needed.
+    if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) {
+        auto* backdropFilterDrawable =
+                mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas());
+        drawDrawable(backdropFilterDrawable);
+    }
+
     drawDrawable(&renderNodeDrawable);
 
     // use staging property, since recording on UI thread
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 596bd37..f67042b 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -20,14 +20,17 @@
 #include <SkBlendMode.h>
 #include <SkClipStack.h>
 #include <SkSurface_Base.h>
+#include <include/effects/SkImageFilters.h>
 #include <string.h>
+
 #include "AnimationContext.h"
 #include "DamageAccumulator.h"
 #include "FatalTestCanvas.h"
 #include "IContextFactory.h"
-#include "hwui/Paint.h"
 #include "RecordingCanvas.h"
 #include "SkiaCanvas.h"
+#include "hwui/Paint.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaPipeline.h"
@@ -1210,3 +1213,77 @@
     canvas.drawDrawable(&drawable);
     EXPECT_EQ(2, canvas.mDrawCounter);
 }
+
+// Verify drawing logics for BackdropFilterDrawable
+RENDERTHREAD_TEST(BackdropFilterDrawable, drawing) {
+    static const int CANVAS_WIDTH = 100;
+    static const int CANVAS_HEIGHT = 200;
+    class SimpleTestCanvas : public TestCanvasBase {
+    public:
+        SkRect mDstBounds;
+        SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+        void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+            // did nothing.
+        }
+
+        // called when BackdropFilterDrawable is drawn.
+        void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
+                              const SkSamplingOptions&, const SkPaint*,
+                              SrcRectConstraint) override {
+            mDrawCounter++;
+            mDstBounds = dst;
+        }
+    };
+    class SimpleLayer : public SkSurface_Base {
+    public:
+        SimpleLayer()
+                : SkSurface_Base(SkImageInfo::MakeN32Premul(CANVAS_WIDTH, CANVAS_HEIGHT), nullptr) {
+        }
+        virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
+            SkBitmap bitmap;
+            bitmap.allocN32Pixels(CANVAS_WIDTH, CANVAS_HEIGHT);
+            bitmap.setImmutable();
+            return SkImage::MakeFromBitmap(bitmap);
+        }
+        SkCanvas* onNewCanvas() override { return new SimpleTestCanvas(); }
+        sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
+        bool onCopyOnWrite(ContentChangeMode) override { return true; }
+        void onWritePixels(const SkPixmap&, int x, int y) {}
+    };
+
+    auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                                          [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+                                              canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+                                                              Paint());
+                                          });
+
+    sk_sp<SkSurface> surface(new SimpleLayer());
+    auto* canvas = reinterpret_cast<SimpleTestCanvas*>(surface->getCanvas());
+    RenderNodeDrawable drawable(node.get(), canvas, true);
+    BackdropFilterDrawable backdropDrawable(node.get(), canvas);
+    canvas->drawDrawable(&drawable);
+    canvas->drawDrawable(&backdropDrawable);
+    // no backdrop filter, skip drawing.
+    EXPECT_EQ(0, canvas->mDrawCounter);
+
+    sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
+    node->animatorProperties().mutateLayerProperties().setBackdropImageFilter(filter.get());
+    canvas->drawDrawable(&drawable);
+    canvas->drawDrawable(&backdropDrawable);
+    // backdrop filter is set, ok to draw.
+    EXPECT_EQ(1, canvas->mDrawCounter);
+    EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds);
+
+    canvas->translate(30, 30);
+    canvas->drawDrawable(&drawable);
+    canvas->drawDrawable(&backdropDrawable);
+    // the drawable is still visible, ok to draw.
+    EXPECT_EQ(2, canvas->mDrawCounter);
+    EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH - 30, CANVAS_HEIGHT - 30), canvas->mDstBounds);
+
+    canvas->translate(CANVAS_WIDTH, CANVAS_HEIGHT);
+    canvas->drawDrawable(&drawable);
+    canvas->drawDrawable(&backdropDrawable);
+    // the drawable is invisible, skip drawing.
+    EXPECT_EQ(2, canvas->mDrawCounter);
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 6031ef7..94fce79 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -122,11 +122,6 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-
-    // Workaround Clang LTO crash.
-    lto: {
-        never: true,
-    },
 }
 
 cc_library_shared {
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 8b5b726..cf5059c 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -44,9 +44,4 @@
         "-Wunreachable-code",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
-
-    // Workaround Clang LTO crash.
-    lto: {
-        never: true,
-    },
 }
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index cb74cfc..fbdfe3e 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -54,6 +54,8 @@
 ?application/ttml+xml ttml dfxp
 ?application/vnd.android.ota ota
 ?application/vnd.apple.mpegurl m3u8
+?application/vnd.apple.pkpass pkpass
+?application/vnd.apple.pkpasses pkpasses
 ?application/vnd.ms-pki.stl stl
 ?application/vnd.ms-powerpoint pot
 ?application/vnd.ms-wpl wpl
diff --git a/packages/CrashRecovery/OWNERS b/packages/CrashRecovery/OWNERS
new file mode 100644
index 0000000..daa0211
--- /dev/null
+++ b/packages/CrashRecovery/OWNERS
@@ -0,0 +1,3 @@
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
new file mode 100644
index 0000000..b2af315
--- /dev/null
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+    name: "framework-crashrecovery-sources",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    path: "java",
+    visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
similarity index 100%
rename from core/java/android/service/watchdog/ExplicitHealthCheckService.java
rename to packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
similarity index 96%
rename from core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
rename to packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
index 78c0328..9096509 100644
--- a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -21,6 +21,7 @@
 /**
  * @hide
  */
+@PermissionManuallyEnforced
 oneway interface IExplicitHealthCheckService
 {
     void setCallback(in @nullable RemoteCallback callback);
diff --git a/core/java/android/service/watchdog/OWNERS b/packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
similarity index 100%
rename from core/java/android/service/watchdog/OWNERS
rename to packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
diff --git a/core/java/android/service/watchdog/PackageConfig.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
similarity index 100%
rename from core/java/android/service/watchdog/PackageConfig.aidl
rename to packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
new file mode 100644
index 0000000..27ddff9
--- /dev/null
+++ b/packages/CrashRecovery/services/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+    name: "services-crashrecovery-sources",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    path: "java",
+    visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
similarity index 100%
rename from services/core/java/com/android/server/ExplicitHealthCheckController.java
rename to packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
similarity index 100%
rename from services/core/java/com/android/server/PackageWatchdog.java
rename to packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
similarity index 100%
rename from services/core/java/com/android/server/RescueParty.java
rename to packages/CrashRecovery/services/java/com/android/server/RescueParty.java
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
rename to packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
rename to packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/services/Android.bp b/services/Android.bp
index 0f6b984..e4fe581 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -43,7 +43,10 @@
     name: "system_optimized_java_defaults",
     module_type: "java_defaults",
     config_namespace: "ANDROID",
-    bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
+    bool_variables: [
+        "SYSTEM_OPTIMIZE_JAVA",
+        "FULL_SYSTEM_OPTIMIZE_JAVA",
+    ],
     properties: ["optimize"],
 }
 
@@ -55,6 +58,7 @@
                 enabled: true,
                 // TODO(b/210510433): Enable optimizations after improving
                 // retracing infra.
+                // See also FULL_SYSTEM_OPTIMIZE_JAVA.
                 optimize: false,
                 shrink: true,
                 ignore_warnings: false,
@@ -78,6 +82,12 @@
                 },
             },
         },
+        // Allow form factors to opt-in full system java optimization
+        FULL_SYSTEM_OPTIMIZE_JAVA: {
+            optimize: {
+                optimize: true,
+            },
+        },
     },
 }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 5295ec8..4fe0592 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -2,7 +2,7 @@
 
 set noparent
 
-ogunwale@google.com
-michaelwr@google.com
+marvinramin@google.com
 vladokom@google.com
-marvinramin@google.com
\ No newline at end of file
+ogunwale@google.com
+michaelwr@google.com
\ No newline at end of file
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 22f8570..77b8b02 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -122,6 +122,9 @@
         "java/com/android/server/am/EventLogTags.logtags",
         "java/com/android/server/wm/EventLogTags.logtags",
         "java/com/android/server/policy/EventLogTags.logtags",
+
+        // Java/AIDL sources to be moved out to CrashRecovery module
+        ":services-crashrecovery-sources",
     ],
 
     libs: [
@@ -153,7 +156,7 @@
         "android.hardware.boot-V1.0-java", // HIDL
         "android.hardware.boot-V1.1-java", // HIDL
         "android.hardware.boot-V1.2-java", // HIDL
-        "android.hardware.boot-V1-java",   // AIDL
+        "android.hardware.boot-V1-java", // AIDL
         "android.hardware.broadcastradio-V2.0-java", // HIDL
         "android.hardware.broadcastradio-V1-java", // AIDL
         "android.hardware.health-V1.0-java", // HIDL
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 572e9c2..926d7a4 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -62,6 +62,7 @@
 import java.nio.file.attribute.PosixFilePermissions;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -328,9 +329,11 @@
      * @param tombstone path to the tombstone
      * @param proto whether the tombstone is stored as proto
      * @param processName the name of the process corresponding to the tombstone
+     * @param tmpFileLock the lock for reading/writing tmp files
      */
     public static void addTombstoneToDropBox(
-                Context ctx, File tombstone, boolean proto, String processName) {
+                Context ctx, File tombstone, boolean proto, String processName,
+                ReentrantLock tmpFileLock) {
         final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
         if (db == null) {
             Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
@@ -351,39 +354,11 @@
                     // due to rate limiting. Do this by enclosing the proto tombsstone in a
                     // container proto that has the dropped entry count and the proto tombstone as
                     // bytes (to avoid the complexity of reading and writing nested protos).
-
-                    // Read the proto tombstone file as bytes.
-                    final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
-
-                    final File tombstoneProtoWithHeaders = File.createTempFile(
-                            tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
-                    Files.setPosixFilePermissions(
-                            tombstoneProtoWithHeaders.toPath(),
-                            PosixFilePermissions.fromString("rw-rw----"));
-
-                    // Write the new proto container proto with headers.
-                    ParcelFileDescriptor pfd;
+                    tmpFileLock.lock();
                     try {
-                        pfd = ParcelFileDescriptor.open(tombstoneProtoWithHeaders, MODE_READ_WRITE);
-
-                        ProtoOutputStream protoStream = new ProtoOutputStream(
-                                pfd.getFileDescriptor());
-                        protoStream.write(TombstoneWithHeadersProto.TOMBSTONE, tombstoneBytes);
-                        protoStream.write(
-                                TombstoneWithHeadersProto.DROPPED_COUNT,
-                                rateLimitResult.droppedCountSinceRateLimitActivated());
-                        protoStream.flush();
-
-                        // Add the proto to dropbox.
-                        db.addFile(TAG_TOMBSTONE_PROTO_WITH_HEADERS, tombstoneProtoWithHeaders, 0);
-                    } catch (FileNotFoundException ex) {
-                        Slog.e(TAG, "failed to open for write: " + tombstoneProtoWithHeaders, ex);
-                        throw ex;
+                        addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
                     } finally {
-                        // Remove the temporary file.
-                        if (tombstoneProtoWithHeaders != null) {
-                            tombstoneProtoWithHeaders.delete();
-                        }
+                        tmpFileLock.unlock();
                     }
                 }
             } else {
@@ -399,6 +374,44 @@
         writeTimestamps(timestamps);
     }
 
+    private static void addAugmentedProtoToDropbox(
+                File tombstone, DropBoxManager db,
+                DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
+        // Read the proto tombstone file as bytes.
+        final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+
+        final File tombstoneProtoWithHeaders = File.createTempFile(
+                tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
+        Files.setPosixFilePermissions(
+                tombstoneProtoWithHeaders.toPath(),
+                PosixFilePermissions.fromString("rw-rw----"));
+
+        // Write the new proto container proto with headers.
+        try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                    tombstoneProtoWithHeaders, MODE_READ_WRITE)) {
+            ProtoOutputStream protoStream =
+                    new ProtoOutputStream(pfd.getFileDescriptor());
+            protoStream.write(TombstoneWithHeadersProto.TOMBSTONE, tombstoneBytes);
+            protoStream.write(
+                    TombstoneWithHeadersProto.DROPPED_COUNT,
+                    rateLimitResult.droppedCountSinceRateLimitActivated());
+            protoStream.flush();
+
+            // Add the proto to dropbox.
+            db.addFile(TAG_TOMBSTONE_PROTO_WITH_HEADERS, tombstoneProtoWithHeaders, 0);
+        } catch (FileNotFoundException ex) {
+            Slog.e(TAG, "failed to open for write: " + tombstoneProtoWithHeaders, ex);
+            throw ex;
+        } catch (IOException ex) {
+            Slog.e(TAG, "IO exception during write: " + tombstoneProtoWithHeaders, ex);
+        } finally {
+            // Remove the temporary file and unlock the lock.
+            if (tombstoneProtoWithHeaders != null) {
+                tombstoneProtoWithHeaders.delete();
+            }
+        }
+    }
+
     private static void addLastkToDropBox(
             DropBoxManager db, HashMap<String, Long> timestamps,
             String headers, String footers, String filename, int maxSize,
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 987507f..3aa275c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
 
 # Connectivity / Networking
 per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 0fcec6f..c26fd5d 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -258,13 +258,6 @@
     @Nullable
     public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
             int recordIndex, @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
-        // When updateDeferredStates() has already applied a deferred state to
-        // all pending items, apply to this new broadcast too
-        if (mLastDeferredStates && record.deferUntilActive
-                && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
-            deferredStatesApplyConsumer.accept(record, recordIndex);
-        }
-
         // Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the
         // BroadcastOptions delivery group APIs.
         if (record.isReplacePending()
@@ -287,6 +280,13 @@
         // with implicit responsiveness expectations.
         getQueueForBroadcast(record).addLast(newBroadcastArgs);
         onBroadcastEnqueued(record, recordIndex);
+
+        // When updateDeferredStates() has already applied a deferred state to
+        // all pending items, apply to this new broadcast too
+        if (mLastDeferredStates && shouldBeDeferred()
+                && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
+            deferredStatesApplyConsumer.accept(record, recordIndex);
+        }
         return null;
     }
 
@@ -1144,9 +1144,6 @@
             } else if (mProcessPersistent) {
                 mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS;
                 mRunnableAtReason = REASON_PERSISTENT;
-            } else if (UserHandle.isCore(uid)) {
-                mRunnableAt = runnableAt;
-                mRunnableAtReason = REASON_CORE_UID;
             } else if (mCountOrdered > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -1193,6 +1190,9 @@
                 // is already cached, they'll be deferred on the line above
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
+            } else if (UserHandle.isCore(uid)) {
+                mRunnableAt = runnableAt;
+                mRunnableAtReason = REASON_CORE_UID;
             } else {
                 mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
                 mRunnableAtReason = REASON_NORMAL;
@@ -1221,32 +1221,45 @@
     }
 
     /**
-     * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our
+     * Update {@link BroadcastRecord#DELIVERY_DEFERRED} states of all our
      * pending broadcasts, when needed.
      */
     void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer,
             @NonNull BroadcastConsumer clearConsumer) {
         // When all we have pending is deferred broadcasts, and we're cached,
         // then we want everything to be marked deferred
-        final boolean wantDeferredStates = (mCountDeferred > 0)
-                && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+        final boolean wantDeferredStates = shouldBeDeferred();
 
         if (mLastDeferredStates != wantDeferredStates) {
             mLastDeferredStates = wantDeferredStates;
             if (wantDeferredStates) {
                 forEachMatchingBroadcast((r, i) -> {
-                    return r.deferUntilActive
-                            && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
+                    return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
                 }, applyConsumer, false);
             } else {
                 forEachMatchingBroadcast((r, i) -> {
-                    return r.deferUntilActive
-                            && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+                    return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
                 }, clearConsumer, false);
             }
         }
     }
 
+    void clearDeferredStates(@NonNull BroadcastConsumer clearConsumer) {
+        if (mLastDeferredStates) {
+            mLastDeferredStates = false;
+            forEachMatchingBroadcast((r, i) -> {
+                return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+            }, clearConsumer, false);
+        }
+    }
+
+    @VisibleForTesting
+    boolean shouldBeDeferred() {
+        if (mRunnableAtInvalidated) updateRunnableAt();
+        return mRunnableAtReason == REASON_CACHED
+                || mRunnableAtReason == REASON_CACHED_INFINITE_DEFER;
+    }
+
     /**
      * Check overall health, confirming things are in a reasonable state and
      * that we're not wedged.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index f420619..abec890 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -249,6 +249,7 @@
     private static final int MSG_CHECK_HEALTH = 5;
     private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 6;
     private static final int MSG_PROCESS_FREEZABLE_CHANGED = 7;
+    private static final int MSG_UID_STATE_CHANGED = 8;
 
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
@@ -295,6 +296,19 @@
                 }
                 return true;
             }
+            case MSG_UID_STATE_CHANGED: {
+                final int uid = (int) msg.obj;
+                final int procState = msg.arg1;
+                synchronized (mService) {
+                    if (procState == ActivityManager.PROCESS_STATE_TOP) {
+                        mUidForeground.put(uid, true);
+                    } else {
+                        mUidForeground.delete(uid);
+                    }
+                    refreshProcessQueuesLocked(uid);
+                }
+                return true;
+            }
         }
         return false;
     };
@@ -450,6 +464,10 @@
                 break;
             }
 
+            // Clear the deferred state of broadcasts in this queue as we are just about to
+            // deliver broadcasts to this process.
+            queue.clearDeferredStates(mBroadcastConsumerDeferClear);
+
             // We might not have heard about a newly running process yet, so
             // consider refreshing if we think we're cold
             updateWarmProcess(queue);
@@ -672,7 +690,7 @@
     @Override
     public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) {
         mLocalHandler.removeMessages(MSG_PROCESS_FREEZABLE_CHANGED, app);
-        mLocalHandler.sendMessage(mHandler.obtainMessage(MSG_PROCESS_FREEZABLE_CHANGED, app));
+        mLocalHandler.obtainMessage(MSG_PROCESS_FREEZABLE_CHANGED, app).sendToTarget();
     }
 
     @Override
@@ -1510,12 +1528,14 @@
         r.resultExtras = null;
     };
 
-    private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
+    @VisibleForTesting
+    final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
         setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
                 "mBroadcastConsumerDeferApply");
     };
 
-    private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
+    @VisibleForTesting
+    final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
         setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
                 "mBroadcastConsumerDeferClear");
     };
@@ -1601,14 +1621,9 @@
             @Override
             public void onUidStateChanged(int uid, int procState, long procStateSeq,
                     int capability) {
-                synchronized (mService) {
-                    if (procState == ActivityManager.PROCESS_STATE_TOP) {
-                        mUidForeground.put(uid, true);
-                    } else {
-                        mUidForeground.delete(uid);
-                    }
-                    refreshProcessQueuesLocked(uid);
-                }
+                mLocalHandler.removeMessages(MSG_UID_STATE_CHANGED, uid);
+                mLocalHandler.obtainMessage(MSG_UID_STATE_CHANGED, procState, 0, uid)
+                        .sendToTarget();
             }
         }, ActivityManager.UID_OBSERVER_PROCSTATE,
                 ActivityManager.PROCESS_STATE_TOP, "android");
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index e5616d0..ab0d0d2 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -61,6 +61,7 @@
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * A class to manage native tombstones.
@@ -74,6 +75,8 @@
     private final Handler mHandler;
     private final TombstoneWatcher mWatcher;
 
+    private final ReentrantLock mTmpFileLock = new ReentrantLock();
+
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -112,7 +115,12 @@
 
         // Clean up temporary files if they made it this far (e.g. if system server crashes).
         if (filename.endsWith(".tmp")) {
-            path.delete();
+            mTmpFileLock.lock();
+            try {
+                path.delete();
+            } finally {
+                mTmpFileLock.unlock();
+            }
             return;
         }
 
@@ -128,7 +136,7 @@
         if (parsedTombstone.isPresent()) {
             processName = parsedTombstone.get().getProcessName();
         }
-        BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName);
+        BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
     }
 
     private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index cf1bfc3..fbfe291 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -20,6 +20,7 @@
       "name": "FrameworksServicesTests",
       "options": [
         {"include-filter": "com.android.server.power"},
+        {"exclude-filter": "com.android.server.power.BatteryStatsTests"},
         {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
@@ -38,7 +39,8 @@
     {
       "name": "FrameworksServicesTests",
       "options": [
-        {"include-filter": "com.android.server.power"}
+        {"include-filter": "com.android.server.power"},
+        {"exclude-filter": "com.android.server.power.BatteryStatsTests"}
       ]
     }
   ]
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 27329e2..5dff9ef 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -8203,18 +8203,18 @@
 
         @GuardedBy("mBsi")
         private void ensureMultiStateCounters(long timestampMs) {
-            if (mProcStateTimeMs != null) {
-                return;
+            if (mProcStateTimeMs == null) {
+                mProcStateTimeMs =
+                        new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
+                                PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+                                timestampMs);
             }
-
-            mProcStateTimeMs =
-                    new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
-                            PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
-                            timestampMs);
-            mProcStateScreenOffTimeMs =
-                    new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
-                            PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
-                            timestampMs);
+            if (mProcStateScreenOffTimeMs == null) {
+                mProcStateScreenOffTimeMs =
+                        new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
+                                PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+                                timestampMs);
+            }
         }
 
         @GuardedBy("mBsi")
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6d560e4..f2346e6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6135,6 +6135,7 @@
                 // Avoid resuming activities on secondary displays since we don't want bubble
                 // activities to be resumed while bubble is still collapsed.
                 // TODO(b/113840485): Having keyguard going away state for secondary displays.
+                && display != null
                 && display.isDefaultDisplay) {
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 06978a5..0a5b9eb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3276,7 +3276,9 @@
             // just kill it. And if it is a window of foreground activity, the activity can be
             // restarted automatically if needed.
             Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
-            android.os.Process.killProcess(mSession.mPid);
+            if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+                android.os.Process.killProcess(mSession.mPid);
+            }
         }
     }
 
diff --git a/services/proguard.flags b/services/proguard.flags
index 84f2a52..88561b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -50,6 +50,11 @@
 -keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
 -keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; }
 -keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; }
+-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+-keep,allowaccessmodification class android.app.admin.flags.Flags { *; }
 
 # Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
 -keep public class com.android.server.utils.Slogf { *; }
@@ -102,9 +107,6 @@
 -keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
   <methods>;
 }
--keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl {
-  <methods>;
-}
 -keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
   *** usbDeviceRemoved(...);
   *** usbDeviceAdded(...);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
new file mode 100644
index 0000000..a0beb17
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package com.android.server.am;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.annotation.NonNull;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.TestLooperManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.SparseArray;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Rule;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class BaseBroadcastQueueTest {
+
+    static final int USER_GUEST = 11;
+
+    static final String PACKAGE_ANDROID = "android";
+    static final String PACKAGE_PHONE = "com.android.phone";
+    static final String PACKAGE_RED = "com.example.red";
+    static final String PACKAGE_GREEN = "com.example.green";
+    static final String PACKAGE_BLUE = "com.example.blue";
+    static final String PACKAGE_YELLOW = "com.example.yellow";
+    static final String PACKAGE_ORANGE = "com.example.orange";
+
+    static final String PROCESS_SYSTEM = "system";
+
+    static final String CLASS_RED = "com.example.red.Red";
+    static final String CLASS_GREEN = "com.example.green.Green";
+    static final String CLASS_BLUE = "com.example.blue.Blue";
+    static final String CLASS_YELLOW = "com.example.yellow.Yellow";
+    static final String CLASS_ORANGE = "com.example.orange.Orange";
+
+    static final BroadcastProcessQueue.BroadcastPredicate BROADCAST_PREDICATE_ANY =
+            (r, i) -> true;
+
+    @Rule
+    public final ApplicationExitInfoTest.ServiceThreadRule
+            mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+    @Mock
+    AppOpsService mAppOpsService;
+    @Mock
+    PackageManagerInternal mPackageManagerInt;
+    @Mock
+    UsageStatsManagerInternal mUsageStatsManagerInt;
+    @Mock
+    DropBoxManagerInternal mDropBoxManagerInt;
+    @Mock
+    AlarmManagerInternal mAlarmManagerInt;
+    @Mock
+    ProcessList mProcessList;
+
+    Context mContext;
+    ActivityManagerService mAms;
+    BroadcastConstants mConstants;
+    BroadcastSkipPolicy mSkipPolicy;
+    HandlerThread mHandlerThread;
+    TestLooperManager mLooper;
+    AtomicInteger mNextPid;
+
+    /**
+     * Map from PID to registered registered runtime receivers.
+     */
+    SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mHandlerThread = new HandlerThread(getTag());
+        mHandlerThread.start();
+        // Pause all event processing until a test chooses to resume
+        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+                .acquireLooperManager(mHandlerThread.getLooper()));
+        mNextPid = new AtomicInteger(100);
+
+        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+        LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+        LocalServices.removeServiceForTest(AlarmManagerInternal.class);
+        LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
+        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+        doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+        doAnswer((invocation) -> {
+            return getUidForPackage(invocation.getArgument(0));
+        }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
+
+        final ActivityManagerService realAms = new ActivityManagerService(
+                new TestInjector(mContext), mServiceThreadRule.getThread());
+        realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+        realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+        realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
+        realAms.mPackageManagerInt = mPackageManagerInt;
+        realAms.mUsageStatsService = mUsageStatsManagerInt;
+        realAms.mProcessesReady = true;
+        mAms = spy(realAms);
+
+        mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+        doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
+
+        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+    }
+
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+    }
+
+    static int getUidForPackage(@NonNull String packageName) {
+        switch (packageName) {
+            case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
+            case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
+            case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
+            case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
+            case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+            case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
+            case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
+            default: throw new IllegalArgumentException();
+        }
+    }
+
+    static int getUidForPackage(@NonNull String packageName, int userId) {
+        return UserHandle.getUid(userId, getUidForPackage(packageName));
+    }
+
+    private class TestInjector extends ActivityManagerService.Injector {
+        TestInjector(Context context) {
+            super(context);
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+                                              Handler handler) {
+            return mAppOpsService;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public ProcessList getProcessList(ActivityManagerService service) {
+            return mProcessList;
+        }
+    }
+
+    abstract String getTag();
+
+    static ApplicationInfo makeApplicationInfo(String packageName) {
+        return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
+    }
+
+    static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = packageName;
+        ai.processName = processName;
+        ai.uid = getUidForPackage(packageName, userId);
+        return ai;
+    }
+
+    static ResolveInfo withPriority(ResolveInfo info, int priority) {
+        info.priority = priority;
+        return info;
+    }
+
+    static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
+        filter.setPriority(priority);
+        return filter;
+    }
+
+    static ResolveInfo makeManifestReceiver(String packageName, String name) {
+        return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
+    }
+
+    static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
+        return makeManifestReceiver(packageName, packageName, name, userId);
+    }
+
+    static ResolveInfo makeManifestReceiver(String packageName, String processName,
+            String name, int userId) {
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = packageName;
+        ri.activityInfo.processName = processName;
+        ri.activityInfo.name = name;
+        ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
+        return ri;
+    }
+
+    BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
+        return makeRegisteredReceiver(app, 0);
+    }
+
+    BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+        final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
+        return makeRegisteredReceiver(receiverList, priority);
+    }
+
+    static BroadcastFilter makeRegisteredReceiver(ReceiverList receiverList, int priority) {
+        final IntentFilter filter = new IntentFilter();
+        filter.setPriority(priority);
+        final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
+                receiverList.app.info.packageName, null, null, null, receiverList.uid,
+                receiverList.userId, false, false, true);
+        receiverList.add(res);
+        return res;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index ff04728..2378416 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -31,17 +31,6 @@
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
 import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
 import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
-import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
-import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
-import static com.android.server.am.BroadcastQueueTest.CLASS_RED;
-import static com.android.server.am.BroadcastQueueTest.CLASS_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_BLUE;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_GREEN;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_RED;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
-import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
-import static com.android.server.am.BroadcastQueueTest.withPriority;
 import static com.android.server.am.BroadcastRecord.isReceiverEquals;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -74,17 +63,15 @@
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.BundleMerger;
 import android.os.DropBoxManager;
-import android.os.HandlerThread;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.TestLooperManager;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 
@@ -108,11 +95,12 @@
 import java.util.Objects;
 
 @SmallTest
-public final class BroadcastQueueModernImplTest {
+public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
+    private static final String TAG = "BroadcastQueueModernImplTest";
+
     private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
     private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
 
-    @Mock ActivityManagerService mAms;
     @Mock ProcessRecord mProcess;
 
     @Mock BroadcastProcessQueue mQueue1;
@@ -120,11 +108,6 @@
     @Mock BroadcastProcessQueue mQueue3;
     @Mock BroadcastProcessQueue mQueue4;
 
-    HandlerThread mHandlerThread;
-    TestLooperManager mLooper;
-
-    BroadcastConstants mConstants;
-    private BroadcastSkipPolicy mSkipPolicy;
     BroadcastQueueModernImpl mImpl;
 
     BroadcastProcessQueue mHead;
@@ -136,22 +119,12 @@
 
     @Before
     public void setUp() throws Exception {
-        mHandlerThread = new HandlerThread(getClass().getSimpleName());
-        mHandlerThread.start();
+        super.setUp();
 
-        // Pause all event processing until a test chooses to resume
-        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
-                .acquireLooperManager(mHandlerThread.getLooper()));
-
-        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.DELAY_URGENT_MILLIS = -120_000;
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
         mConstants.DELAY_CACHED_MILLIS = 120_000;
 
-        mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
-        doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
-        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
         final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
             public void addBroadcastToHistoryLocked(BroadcastRecord original) {
                 // Ignored
@@ -169,7 +142,12 @@
 
     @After
     public void tearDown() throws Exception {
-        mHandlerThread.quit();
+        super.tearDown();
+    }
+
+    @Override
+    public String getTag() {
+        return TAG;
     }
 
     /**
@@ -225,11 +203,6 @@
                 List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
     }
 
-    private BroadcastRecord makeOrderedBroadcastRecord(Intent intent) {
-        return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
-                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
-    }
-
     private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) {
         return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false);
     }
@@ -246,8 +219,8 @@
 
     private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
             List receivers, IIntentReceiver resultTo, boolean ordered) {
-        return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
-                null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
+        return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false,
+                null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
                 Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
                 BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
     }
@@ -259,12 +232,12 @@
 
     private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
             BroadcastRecord record, int recordIndex, long enqueueTime) {
-        queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
-            throw new UnsupportedOperationException();
-        });
         record.enqueueTime = enqueueTime;
         record.enqueueRealTime = enqueueTime;
         record.enqueueClockTime = enqueueTime;
+        queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
+            throw new UnsupportedOperationException();
+        });
     }
 
     @Test
@@ -419,6 +392,7 @@
         assertFalse(queue.isRunnable());
         assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
                 queue.getRunnableAtReason());
+        assertTrue(queue.shouldBeDeferred());
         assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
     }
 
@@ -445,6 +419,7 @@
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertTrue(queue.isRunnable());
         assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+        assertTrue(queue.shouldBeDeferred());
         assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
     }
 
@@ -526,11 +501,13 @@
         queue.invalidateRunnableAt();
         assertThat(queue.getRunnableAt()).isGreaterThan(airplaneRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
 
         mConstants.MAX_PENDING_BROADCASTS = 1;
         queue.invalidateRunnableAt();
         assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
     }
 
     @Test
@@ -549,10 +526,12 @@
         queue.setProcessAndUidState(mProcess, true, false);
         assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
 
         queue.setProcessAndUidState(mProcess, false, false);
         assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
     }
 
     @Test
@@ -570,6 +549,7 @@
 
         assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_TOP_PROCESS, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
 
         doReturn(ActivityManager.PROCESS_STATE_SERVICE).when(mProcess).getSetProcState();
         queue.setProcessAndUidState(mProcess, false, false);
@@ -580,6 +560,7 @@
                 List.of(makeMockRegisteredReceiver())), 0);
         assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
     }
 
     @Test
@@ -594,16 +575,19 @@
 
         assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
 
         doReturn(true).when(mProcess).isPersistent();
         queue.setProcessAndUidState(mProcess, false, false);
         assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_PERSISTENT, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
 
         doReturn(false).when(mProcess).isPersistent();
         queue.setProcessAndUidState(mProcess, false, false);
         assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
     }
 
     @Test
@@ -618,6 +602,31 @@
 
         assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
         assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
+    }
+
+    @Test
+    public void testRunnableAt_freezableCoreUid() {
+        final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+                "com.android.bluetooth", Process.BLUETOOTH_UID);
+
+        // Mark the process as freezable
+        queue.setProcessAndUidState(mProcess, false, true);
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true);
+        final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, options,
+                List.of(makeMockRegisteredReceiver()), false);
+        enqueueOrReplaceBroadcast(queue, timeTickRecord, 0);
+
+        assertEquals(Long.MAX_VALUE, queue.getRunnableAt());
+        assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+                queue.getRunnableAtReason());
+        assertTrue(queue.shouldBeDeferred());
+
+        queue.setProcessAndUidState(mProcess, false, false);
+        assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
+        assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+        assertFalse(queue.shouldBeDeferred());
     }
 
     /**
@@ -1553,6 +1562,216 @@
         verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
     }
 
+    @Test
+    public void testDeliveryDeferredForCached() throws Exception {
+        final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+        final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+                List.of(makeRegisteredReceiver(greenProcess, 0)));
+
+        final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        final BroadcastOptions optionsBatteryChanged =
+                BroadcastOptions.makeWithDeferUntilActive(true);
+        final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+                optionsBatteryChanged,
+                List.of(makeRegisteredReceiver(greenProcess, 10),
+                        makeRegisteredReceiver(redProcess, 0)),
+                false /* ordered */);
+
+        mImpl.enqueueBroadcastLocked(timeTickRecord);
+        mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+        final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+                getUidForPackage(PACKAGE_RED));
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+        assertFalse(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // Simulate process state change
+        greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+                true /* processFreezable */);
+        greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+                mImpl.mBroadcastConsumerDeferClear);
+
+        assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+        assertTrue(greenQueue.shouldBeDeferred());
+        // Once the broadcasts to green process are deferred, broadcasts to red process
+        // shouldn't be blocked anymore.
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // All broadcasts to green process should be deferred.
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+
+        final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+        final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+                List.of(makeRegisteredReceiver(greenProcess, 0)));
+        mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+        assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+        assertTrue(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // All broadcasts to the green process, including the newly enqueued one, should be
+        // deferred.
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+
+        // Simulate process state change
+        greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+                false /* processFreezable */);
+        greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+                mImpl.mBroadcastConsumerDeferClear);
+
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+        assertFalse(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+    }
+
+    @Test
+    public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception {
+        final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+        final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true);
+        final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick,
+                List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+
+        final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        final BroadcastOptions optionsBatteryChanged =
+                BroadcastOptions.makeWithDeferUntilActive(true);
+        final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+                optionsBatteryChanged,
+                List.of(makeRegisteredReceiver(greenProcess, 10),
+                        makeRegisteredReceiver(redProcess, 0)),
+                false /* ordered */);
+
+        mImpl.enqueueBroadcastLocked(timeTickRecord);
+        mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+        final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+                getUidForPackage(PACKAGE_RED));
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+        assertFalse(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // Simulate process state change
+        greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+                true /* processFreezable */);
+        greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+                mImpl.mBroadcastConsumerDeferClear);
+
+        assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+                greenQueue.getRunnableAtReason());
+        assertTrue(greenQueue.shouldBeDeferred());
+        // Once the broadcasts to green process are deferred, broadcasts to red process
+        // shouldn't be blocked anymore.
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // All broadcasts to green process should be deferred.
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+
+        final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+        final BroadcastOptions optionsPackageChanged =
+                BroadcastOptions.makeWithDeferUntilActive(true);
+        final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+                optionsPackageChanged,
+                List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+        mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+        assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+                greenQueue.getRunnableAtReason());
+        assertTrue(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        // All broadcasts to the green process, including the newly enqueued one, should be
+        // deferred.
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+
+        // Simulate process state change
+        greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+                false /* processFreezable */);
+        greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+                mImpl.mBroadcastConsumerDeferClear);
+
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+        assertFalse(greenQueue.shouldBeDeferred());
+        assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+        assertFalse(redQueue.shouldBeDeferred());
+
+        greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+        redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+            assertEquals("Unexpected state for " + r,
+                    BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+        }, false /* andRemove */);
+    }
+
+    // TODO: Reuse BroadcastQueueTest.makeActiveProcessRecord()
+    private ProcessRecord makeProcessRecord(ApplicationInfo info) {
+        final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid));
+        r.setPid(mNextPid.incrementAndGet());
+        return r;
+    }
+
+    BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+        final IIntentReceiver receiver = mock(IIntentReceiver.class);
+        final ReceiverList receiverList = new ReceiverList(mAms, app, app.getPid(), app.info.uid,
+                UserHandle.getUserId(app.info.uid), receiver);
+        return makeRegisteredReceiver(receiverList, priority);
+    }
+
     private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
         final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
         packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 73eb237..e914726 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -62,58 +62,36 @@
 import android.app.IApplicationThread;
 import android.app.UidObserver;
 import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.PowerExemptionManager;
 import android.os.SystemClock;
-import android.os.TestLooperManager;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 import android.util.Pair;
-import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.server.AlarmManagerInternal;
-import com.android.server.DropBoxManagerInternal;
-import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.Injector;
-import com.android.server.appop.AppOpsService;
-import com.android.server.wm.ActivityTaskManagerService;
-
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.mockito.verification.VerificationMode;
 
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.Writer;
@@ -125,7 +103,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.UnaryOperator;
 
@@ -135,13 +113,9 @@
 @MediumTest
 @RunWith(Parameterized.class)
 @SuppressWarnings("GuardedBy")
-public class BroadcastQueueTest {
+public class BroadcastQueueTest extends BaseBroadcastQueueTest {
     private static final String TAG = "BroadcastQueueTest";
 
-    @Rule
-    public final ApplicationExitInfoTest.ServiceThreadRule
-            mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
-
     private final Impl mImpl;
 
     private enum Impl {
@@ -149,30 +123,8 @@
         MODERN,
     }
 
-    private Context mContext;
-    private HandlerThread mHandlerThread;
-    private TestLooperManager mLooper;
-    private AtomicInteger mNextPid;
-
-    @Mock
-    private AppOpsService mAppOpsService;
-    @Mock
-    private ProcessList mProcessList;
-    @Mock
-    private DropBoxManagerInternal mDropBoxManagerInt;
-    @Mock
-    private PackageManagerInternal mPackageManagerInt;
-    @Mock
-    private UsageStatsManagerInternal mUsageStatsManagerInt;
-    @Mock
-    private AlarmManagerInternal mAlarmManagerInt;
-
-    private ActivityManagerService mAms;
     private BroadcastQueue mQueue;
-    BroadcastConstants mConstants;
-    private BroadcastSkipPolicy mSkipPolicy;
     private UidObserver mUidObserver;
-    private UidObserver mUidCachedStateObserver;
 
     /**
      * Desired behavior of the next
@@ -182,11 +134,6 @@
             ProcessStartBehavior.SUCCESS);
 
     /**
-     * Map from PID to registered registered runtime receivers.
-     */
-    private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
-
-    /**
      * Collection of all active processes during current test run.
      */
     private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
@@ -207,41 +154,8 @@
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
 
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-
-        // Pause all event processing until a test chooses to resume
-        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
-                .acquireLooperManager(mHandlerThread.getLooper()));
-
-        mNextPid = new AtomicInteger(100);
-
-        LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
-        LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
-        LocalServices.removeServiceForTest(AlarmManagerInternal.class);
-        LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
-        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
-        doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
-        doAnswer((invocation) -> {
-            return getUidForPackage(invocation.getArgument(0));
-        }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
-
-        final ActivityManagerService realAms = new ActivityManagerService(
-                new TestInjector(mContext), mServiceThreadRule.getThread());
-        realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
-        realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
-        realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
-        realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
-        realAms.mPackageManagerInt = mPackageManagerInt;
-        realAms.mUsageStatsService = mUsageStatsManagerInt;
-        realAms.mProcessesReady = true;
-        mAms = spy(realAms);
         doAnswer((invocation) -> {
             Log.v(TAG, "Intercepting startProcessLocked() for "
                     + Arrays.toString(invocation.getArguments()));
@@ -320,21 +234,11 @@
             return null;
         }).when(mAms).registerUidObserver(any(), anyInt(),
                 eq(ActivityManager.PROCESS_STATE_TOP), any());
-        doAnswer((invocation) -> {
-            mUidCachedStateObserver = invocation.getArgument(0);
-            return null;
-        }).when(mAms).registerUidObserver(any(), anyInt(),
-                eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
 
-        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
-        mConstants.TIMEOUT = 100;
+        mConstants.TIMEOUT = 200;
         mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
         mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
 
-        mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
-        doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
-        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
         final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
             public void addBroadcastToHistoryLocked(BroadcastRecord original) {
                 // Ignored
@@ -357,7 +261,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mHandlerThread.quit();
+        super.tearDown();
 
         // Verify that all processes have finished handling broadcasts
         for (ProcessRecord app : mActiveProcesses) {
@@ -368,26 +272,9 @@
         }
     }
 
-    private class TestInjector extends Injector {
-        TestInjector(Context context) {
-            super(context);
-        }
-
-        @Override
-        public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
-                Handler handler) {
-            return mAppOpsService;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandlerThread.getThreadHandler();
-        }
-
-        @Override
-        public ProcessList getProcessList(ActivityManagerService service) {
-            return mProcessList;
-        }
+    @Override
+    public String getTag() {
+        return TAG;
     }
 
     private enum ProcessStartBehavior {
@@ -533,62 +420,6 @@
         return Pair.create(app.getPid(), intent.getAction());
     }
 
-    static ApplicationInfo makeApplicationInfo(String packageName) {
-        return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
-    }
-
-    static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
-        final ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = packageName;
-        ai.processName = processName;
-        ai.uid = getUidForPackage(packageName, userId);
-        return ai;
-    }
-
-    static ResolveInfo withPriority(ResolveInfo info, int priority) {
-        info.priority = priority;
-        return info;
-    }
-
-    static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
-        filter.setPriority(priority);
-        return filter;
-    }
-
-    static ResolveInfo makeManifestReceiver(String packageName, String name) {
-        return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
-    }
-
-    static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
-        return makeManifestReceiver(packageName, packageName, name, userId);
-    }
-
-    static ResolveInfo makeManifestReceiver(String packageName, String processName, String name,
-            int userId) {
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = packageName;
-        ri.activityInfo.processName = processName;
-        ri.activityInfo.name = name;
-        ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
-        return ri;
-    }
-
-    private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
-        return makeRegisteredReceiver(app, 0);
-    }
-
-    private BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
-        final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
-        final IntentFilter filter = new IntentFilter();
-        filter.setPriority(priority);
-        final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
-                receiverList.app.info.packageName, null, null, null, receiverList.uid,
-                receiverList.userId, false, false, true);
-        receiverList.add(res);
-        return res;
-    }
-
     private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
             List<Object> receivers) {
         return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
@@ -707,6 +538,9 @@
     private void waitForIdle() throws Exception {
         mLooper.release();
         mQueue.waitForIdle(LOG_WRITER_INFO);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mHandlerThread.getThreadHandler().post(latch::countDown);
+        latch.await();
         mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
                 .acquireLooperManager(mHandlerThread.getLooper()));
     }
@@ -772,41 +606,6 @@
                 eq(userId), anyInt(), anyInt(), any());
     }
 
-    static final int USER_GUEST = 11;
-
-    static final String PACKAGE_ANDROID = "android";
-    static final String PACKAGE_PHONE = "com.android.phone";
-    static final String PACKAGE_RED = "com.example.red";
-    static final String PACKAGE_GREEN = "com.example.green";
-    static final String PACKAGE_BLUE = "com.example.blue";
-    static final String PACKAGE_YELLOW = "com.example.yellow";
-    static final String PACKAGE_ORANGE = "com.example.orange";
-
-    static final String PROCESS_SYSTEM = "system";
-
-    static final String CLASS_RED = "com.example.red.Red";
-    static final String CLASS_GREEN = "com.example.green.Green";
-    static final String CLASS_BLUE = "com.example.blue.Blue";
-    static final String CLASS_YELLOW = "com.example.yellow.Yellow";
-    static final String CLASS_ORANGE = "com.example.orange.Orange";
-
-    static int getUidForPackage(@NonNull String packageName) {
-        switch (packageName) {
-            case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
-            case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
-            case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
-            case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
-            case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
-            case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
-            case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
-            default: throw new IllegalArgumentException();
-        }
-    }
-
-    static int getUidForPackage(@NonNull String packageName, int userId) {
-        return UserHandle.getUid(userId, getUidForPackage(packageName));
-    }
-
     /**
      * Baseline verification of common debugging infrastructure, mostly to make
      * sure it doesn't crash.
@@ -2342,6 +2141,7 @@
 
         mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
                 ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+        waitForIdle();
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
@@ -2375,6 +2175,7 @@
 
         mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
                 ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+        waitForIdle();
 
         final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c152a41..25bd14f 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2951,11 +2951,11 @@
 
         for(String key : bundle.keySet()) {
             if (key != null) {
-                final Object value = bundle.get(key);
-                final Object newValue = newBundle.get(key);
                 if (!newBundle.containsKey(key)) {
                     return false;
                 }
+                final Object value = bundle.get(key);
+                final Object newValue = newBundle.get(key);
                 if (value instanceof Bundle && newValue instanceof Bundle) {
                     if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) {
                         return false;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 80c7a21..db3a992 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -789,6 +789,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="BackdropBlurActivity"
+                  android:label="RenderEffect/BackdropBlur"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="BlurActivity"
                   android:label="RenderEffect/Blur"
                   android:exported="true">
diff --git a/tests/HwAccelerationTest/res/drawable/robot_repeated.xml b/tests/HwAccelerationTest/res/drawable/robot_repeated.xml
new file mode 100644
index 0000000..bbb15b7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/robot_repeated.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@drawable/robot"
+        android:tileMode="repeat" android:gravity="fill" />
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
new file mode 100644
index 0000000..8086b29
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Outline;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BackdropBlurActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final ScrollView scrollView = new ScrollView(this);
+        final FrameLayout innerFrame = new FrameLayout(this);
+        final View backgroundView = new View(this);
+        backgroundView.setBackgroundResource(R.drawable.robot_repeated);
+        innerFrame.addView(backgroundView, ViewGroup.LayoutParams.MATCH_PARENT, 10000);
+        scrollView.addView(innerFrame,
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        final FrameLayout contentView = new FrameLayout(this);
+        contentView.addView(scrollView,
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        contentView.addView(new BackdropBlurView(this), 300, 300);
+        setContentView(contentView);
+    }
+
+    private static class BackdropBlurView extends View {
+        private final float mBlurRadius = 60f;
+        private final float mSaturation = 1.8f;
+
+        private float mDownOffsetX;
+        private float mDownOffsetY;
+
+        BackdropBlurView(Context c) {
+            super(c);
+
+            // init RenderEffect.
+            final RenderEffect blurEffect = RenderEffect.createBlurEffect(
+                    mBlurRadius, mBlurRadius,
+                    null, Shader.TileMode.MIRROR // TileMode.MIRROR is better for blur.
+            );
+
+            final ColorMatrix colorMatrix = new ColorMatrix();
+            colorMatrix.setSaturation(mSaturation);
+            final RenderEffect effect = RenderEffect.createColorFilterEffect(
+                    new ColorMatrixColorFilter(colorMatrix), blurEffect
+            );
+            setBackdropRenderEffect(effect);
+
+            // clip to a round outline.
+            setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View v, Outline outline) {
+                    outline.setOval(0, 0, v.getWidth(), v.getHeight());
+                }
+            });
+            setClipToOutline(true);
+
+            animate().setInterpolator(new DecelerateInterpolator(2.0f));
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawColor(0x99F0F0F0);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mDownOffsetX = event.getRawX() - getTranslationX();
+                    mDownOffsetY = event.getRawY() - getTranslationY();
+                    animate().scaleX(1.5f).scaleY(1.5f).start();
+                    break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    animate().scaleX(1f).scaleY(1f).start();
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    setTranslationX(event.getRawX() - mDownOffsetX);
+                    setTranslationY(event.getRawY() - mDownOffsetY);
+                    break;
+            }
+            return true;
+        }
+    }
+}