Merge "add API for getRegisteredPhoneAccounts" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 25765cf..84fbe1a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2167,11 +2167,6 @@
     field public static final int notification_large_icon_width = 17104901; // 0x1050005
     field public static final int system_app_widget_background_radius = 17104904; // 0x1050008
     field public static final int system_app_widget_inner_radius = 17104905; // 0x1050009
-    field public static final int system_corner_radius_large;
-    field public static final int system_corner_radius_medium;
-    field public static final int system_corner_radius_small;
-    field public static final int system_corner_radius_xlarge;
-    field public static final int system_corner_radius_xsmall;
     field public static final int thumbnail_height = 17104897; // 0x1050001
     field public static final int thumbnail_width = 17104898; // 0x1050002
   }
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 24a5157..6255260 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -550,7 +550,7 @@
     @UnsupportedAppUsage
     protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
             @Nullable ApkAssetsSupplier apkSupplier) {
-        final AssetManager.Builder builder = new AssetManager.Builder();
+        final AssetManager.Builder builder = new AssetManager.Builder().setNoInit();
 
         final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
         for (int i = 0, n = apkKeys.size(); i < n; i++) {
@@ -1555,7 +1555,7 @@
         } else if(overlayPaths == null) {
             return ArrayUtils.cloneOrNull(resourceDirs);
         } else {
-            final ArrayList<String> paths = new ArrayList<>();
+            final var paths = new ArrayList<String>(overlayPaths.length + resourceDirs.length);
             for (final String path : overlayPaths) {
                 paths.add(path);
             }
diff --git a/core/java/android/content/pm/ArchivedActivityInfo.java b/core/java/android/content/pm/ArchivedActivityInfo.java
index 166d265..9f65f589 100644
--- a/core/java/android/content/pm/ArchivedActivityInfo.java
+++ b/core/java/android/content/pm/ArchivedActivityInfo.java
@@ -24,6 +24,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.util.Slog;
 
 import com.android.internal.util.DataClass;
 
@@ -39,6 +40,7 @@
 @DataClass(genBuilder = false, genConstructor = false, genSetters = true)
 @FlaggedApi(Flags.FLAG_ARCHIVING)
 public final class ArchivedActivityInfo {
+    private static final String TAG = "ArchivedActivityInfo";
     /** The label for the activity. */
     private @NonNull CharSequence mLabel;
     /** The component name of this activity. */
@@ -138,7 +140,8 @@
                 bitmap.getByteCount())) {
             bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
             return baos.toByteArray();
-        } catch (IOException ignored) {
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to compress bitmap", e);
             return null;
         }
     }
@@ -240,10 +243,10 @@
     }
 
     @DataClass.Generated(
-            time = 1705615445673L,
+            time = 1708042076897L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static  byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static  android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static  byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static  android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23b9d0b..d259e97 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -137,6 +137,8 @@
         private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
         private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>();
 
+        private boolean mNoInit = false;
+
         public Builder addApkAssets(ApkAssets apkAssets) {
             mUserApkAssets.add(apkAssets);
             return this;
@@ -147,6 +149,11 @@
             return this;
         }
 
+        public Builder setNoInit() {
+            mNoInit = true;
+            return this;
+        }
+
         public AssetManager build() {
             // Retrieving the system ApkAssets forces their creation as well.
             final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
@@ -188,7 +195,7 @@
             final AssetManager assetManager = new AssetManager(false /*sentinel*/);
             assetManager.mApkAssets = apkAssets;
             AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
-                    false /*invalidateCaches*/);
+                    false /*invalidateCaches*/, mNoInit /*preset*/);
             assetManager.mLoaders = mLoaders.isEmpty() ? null
                     : mLoaders.toArray(new ResourcesLoader[0]);
 
@@ -329,7 +336,7 @@
         synchronized (this) {
             ensureOpenLocked();
             mApkAssets = newApkAssets;
-            nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+            nativeSetApkAssets(mObject, mApkAssets, invalidateCaches, false);
             if (invalidateCaches) {
                 // Invalidate all caches.
                 invalidateCachesLocked(-1);
@@ -496,7 +503,7 @@
 
             mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
             mApkAssets[count] = assets;
-            nativeSetApkAssets(mObject, mApkAssets, true);
+            nativeSetApkAssets(mObject, mApkAssets, true, false);
             invalidateCachesLocked(-1);
             return count + 1;
         }
@@ -1503,12 +1510,29 @@
             int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
             int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
             int grammaticalGender, int majorVersion) {
+        setConfigurationInternal(mcc, mnc, defaultLocale, locales, orientation,
+                touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
+                screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
+                screenLayout, uiMode, colorMode, grammaticalGender, majorVersion, false);
+    }
+
+    /**
+     * Change the configuration used when retrieving resources, and potentially force a refresh of
+     * the state.  Not for use by applications.
+     * @hide
+     */
+    void setConfigurationInternal(int mcc, int mnc, String defaultLocale, String[] locales,
+            int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
+            int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
+            int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
+            int grammaticalGender, int majorVersion, boolean forceRefresh) {
         synchronized (this) {
             ensureValidLocked();
             nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
                     touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
                     screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
-                    screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
+                    screenLayout, uiMode, colorMode, grammaticalGender, majorVersion,
+                    forceRefresh);
         }
     }
 
@@ -1593,13 +1617,13 @@
     private static native long nativeCreate();
     private static native void nativeDestroy(long ptr);
     private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
-            boolean invalidateCaches);
+            boolean invalidateCaches, boolean preset);
     private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
             @Nullable String defaultLocale, @NonNull String[] locales, int orientation,
             int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
             int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
             int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
-            int majorVersion);
+            int majorVersion, boolean forceRefresh);
     private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
             long ptr, boolean includeOverlays, boolean includeLoaders);
 
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 5e442b8..079c2c1 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -200,7 +200,7 @@
         mMetrics.setToDefaults();
         mDisplayAdjustments = displayAdjustments;
         mConfiguration.setToDefaults();
-        updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
+        updateConfigurationImpl(config, metrics, displayAdjustments.getCompatibilityInfo(), true);
     }
 
     public DisplayAdjustments getDisplayAdjustments() {
@@ -402,7 +402,12 @@
     }
 
     public void updateConfiguration(Configuration config, DisplayMetrics metrics,
-                                    CompatibilityInfo compat) {
+            CompatibilityInfo compat) {
+        updateConfigurationImpl(config, metrics, compat, false);
+    }
+
+    private void updateConfigurationImpl(Configuration config, DisplayMetrics metrics,
+                                    CompatibilityInfo compat, boolean forceAssetsRefresh) {
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
         try {
             synchronized (mAccessLock) {
@@ -528,7 +533,7 @@
                     keyboardHidden = mConfiguration.keyboardHidden;
                 }
 
-                mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+                mAssets.setConfigurationInternal(mConfiguration.mcc, mConfiguration.mnc,
                         defaultLocale,
                         selectedLocales,
                         mConfiguration.orientation,
@@ -539,7 +544,7 @@
                         mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
                         mConfiguration.screenLayout, mConfiguration.uiMode,
                         mConfiguration.colorMode, mConfiguration.getGrammaticalGender(),
-                        Build.VERSION.RESOURCES_SDK_INT);
+                        Build.VERSION.RESOURCES_SDK_INT, forceAssetsRefresh);
 
                 if (DEBUG_CONFIG) {
                     Slog.i(TAG, "**** Updating config of " + this + ": final config is "
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 5cbbbef..2226881 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -58,6 +58,7 @@
 
     private final boolean mIsOwner;
     private final long mMemoryAddr;
+    private final int mSize;
     private int mFd = -1;
 
     /**
@@ -75,6 +76,9 @@
         final String name = UUID.randomUUID().toString();
         mFd = nativeCreate(name, size);
         mMemoryAddr = nativeOpen(mFd, mIsOwner);
+        // Note that we use the effective size after allocation, rather than the provided size,
+        // preserving compat with the original behavior. In practice these should be equivalent.
+        mSize = nativeSize(mFd);
         mCloseGuard.open("MemoryIntArray.close");
     }
 
@@ -86,6 +90,7 @@
         }
         mFd = pfd.detachFd();
         mMemoryAddr = nativeOpen(mFd, mIsOwner);
+        mSize = nativeSize(mFd);
         mCloseGuard.open("MemoryIntArray.close");
     }
 
@@ -127,13 +132,11 @@
     }
 
     /**
-     * Gets the array size.
-     *
-     * @throws IOException If an error occurs while accessing the shared memory.
+     * @return Gets the array size.
      */
-    public int size() throws IOException {
+    public int size() {
         enforceNotClosed();
-        return nativeSize(mFd);
+        return mSize;
     }
 
     /**
@@ -210,11 +213,10 @@
         }
     }
 
-    private void enforceValidIndex(int index) throws IOException {
-        final int size = size();
-        if (index < 0 || index > size - 1) {
+    private void enforceValidIndex(int index) {
+        if (index < 0 || index > mSize - 1) {
             throw new IndexOutOfBoundsException(
-                    index + " not between 0 and " + (size - 1));
+                    index + " not between 0 and " + (mSize - 1));
         }
     }
 
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 4fe53c2..a8d4e2d 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -579,6 +579,17 @@
     }
 
     /**
+     * Get the combining character that corresponds with the provided accent.
+     *
+     * @param accent The accent character.  eg. '`'
+     * @return The combining character
+     * @hide
+     */
+    public static int getCombiningChar(int accent) {
+        return sAccentToCombining.get(accent);
+    }
+
+    /**
      * Describes the character mappings associated with a key.
      *
      * @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3ee15ab..3d0ab4e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -314,7 +314,8 @@
 }
 
 static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
-                               jobjectArray apk_assets_array, jboolean invalidate_caches) {
+                               jobjectArray apk_assets_array, jboolean invalidate_caches,
+                               jboolean preset) {
   ATRACE_NAME("AssetManager::SetApkAssets");
 
   const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
@@ -343,7 +344,11 @@
   }
 
   auto assetmanager = LockAndStartAssetManager(ptr);
-  assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+  if (preset) {
+    assetmanager->PresetApkAssets(apk_assets);
+  } else {
+    assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+  }
 }
 
 static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
@@ -353,7 +358,7 @@
                                    jint screen_height, jint smallest_screen_width_dp,
                                    jint screen_width_dp, jint screen_height_dp, jint screen_layout,
                                    jint ui_mode, jint color_mode, jint grammatical_gender,
-                                   jint major_version) {
+                                   jint major_version, jboolean force_refresh) {
   ATRACE_NAME("AssetManager::SetConfiguration");
 
   const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
@@ -413,7 +418,7 @@
   }
 
   auto assetmanager = LockAndStartAssetManager(ptr);
-  assetmanager->SetConfigurations(configs);
+  assetmanager->SetConfigurations(std::move(configs), force_refresh != JNI_FALSE);
   assetmanager->SetDefaultLocale(default_locale_int);
 }
 
@@ -1522,8 +1527,8 @@
         // AssetManager setup methods.
         {"nativeCreate", "()J", (void*)NativeCreate},
         {"nativeDestroy", "(J)V", (void*)NativeDestroy},
-        {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
-        {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
+        {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;ZZ)V", (void*)NativeSetApkAssets},
+        {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIIIZ)V",
          (void*)NativeSetConfiguration},
         {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
          (void*)NativeGetAssignedPackageIdentifiers},
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index fa15c3f..972fe7e 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -204,11 +204,4 @@
     <dimen name="progress_bar_size_small">16dip</dimen>
     <dimen name="progress_bar_size_medium">48dp</dimen>
     <dimen name="progress_bar_size_large">76dp</dimen>
-
-    <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
-    <dimen name="system_corner_radius_xsmall">4dp</dimen>
-    <dimen name="system_corner_radius_small">8dp</dimen>
-    <dimen name="system_corner_radius_medium">16dp</dimen>
-    <dimen name="system_corner_radius_large">26dp</dimen>
-    <dimen name="system_corner_radius_xlarge">36dp</dimen>
 </resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index c797210..5987f6e 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -184,11 +184,11 @@
 
   <staging-public-group type="dimen" first-id="0x01b90000">
     <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
-    <public name="system_corner_radius_xsmall" />
-    <public name="system_corner_radius_small" />
-    <public name="system_corner_radius_medium" />
-    <public name="system_corner_radius_large" />
-    <public name="system_corner_radius_xlarge" />
+    <public name="removed_system_corner_radius_xsmall" />
+    <public name="removed_system_corner_radius_small" />
+    <public name="removed_system_corner_radius_medium" />
+    <public name="removed_system_corner_radius_large" />
+    <public name="removed_system_corner_radius_xlarge" />
   </staging-public-group>
 
   <staging-public-group type="color" first-id="0x01b80000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7c290b1..9d7acff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5356,11 +5356,4 @@
   <java-symbol type="drawable" name="ic_satellite_alt_24px" />
 
   <java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
-
-  <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
-  <java-symbol type="dimen" name="system_corner_radius_xsmall" />
-  <java-symbol type="dimen" name="system_corner_radius_small" />
-  <java-symbol type="dimen" name="system_corner_radius_medium" />
-  <java-symbol type="dimen" name="system_corner_radius_large" />
-  <java-symbol type="dimen" name="system_corner_radius_xlarge" />
 </resources>
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 51013e4..8093af9 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -123,7 +123,7 @@
             parcel.recycle();
 
             assertNotNull("Should marshall file descriptor", secondArray);
-
+            assertEquals("Marshalled size must be three", 3, secondArray.size());
             assertEquals("First element should be 1", 1, secondArray.get(0));
             assertEquals("First element should be 2", 2, secondArray.get(1));
             assertEquals("First element should be 3", 3, secondArray.get(2));
diff --git a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
index 9264c6c..32dda6b 100644
--- a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
+++ b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
@@ -84,11 +84,7 @@
             @Override
             public int size() {
                 synchronized (mLock) {
-                    try {
-                        return mArray.size();
-                    } catch (IOException e) {
-                        throw new IllegalStateException(e);
-                    }
+                    return mArray.size();
                 }
             }
 
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
index d763598..afec35c 100644
--- a/graphics/java/android/graphics/pdf/TEST_MAPPING
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -1,7 +1,12 @@
 {
   "presubmit": [
     {
-      "name": "CtsPdfTestCases"
+      "name": "CtsPdfTestCases",
+      "options": [
+        {
+          "include-filter": "android.graphics.pdf.cts.PdfDocumentTest"
+        }
+      ]
     }
   ]
 }
diff --git a/libs/WindowManager/Shell/aconfig/OWNERS b/libs/WindowManager/Shell/aconfig/OWNERS
new file mode 100644
index 0000000..9eba0f2
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/OWNERS
@@ -0,0 +1,3 @@
+# Owners for flag changes
+madym@google.com
+hwwang@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 9a66c0f..0967f4e 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -64,3 +64,10 @@
     description: "Enables the new bubble bar UI for tablets"
     bug: "286246694"
 }
+
+flag {
+    name: "enable_bubbles_long_press_nav_handle"
+    namespace: "multitasking"
+    description: "Enables long-press action for nav handle when a bubble is expanded"
+    bug: "324910035"
+}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8748dab..46f636e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -117,6 +117,10 @@
   return true;
 }
 
+void AssetManager2::PresetApkAssets(ApkAssetsList apk_assets) {
+  BuildDynamicRefTable(apk_assets);
+}
+
 bool AssetManager2::SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,
                                  bool invalidate_caches) {
   return SetApkAssets(ApkAssetsList(apk_assets.begin(), apk_assets.size()), invalidate_caches);
@@ -432,13 +436,18 @@
   return false;
 }
 
-void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations,
+    bool force_refresh) {
   int diff = 0;
-  if (configurations_.size() != configurations.size()) {
+  if (force_refresh) {
     diff = -1;
   } else {
-    for (int i = 0; i < configurations_.size(); i++) {
-      diff |= configurations_[i].diff(configurations[i]);
+    if (configurations_.size() != configurations.size()) {
+      diff = -1;
+    } else {
+      for (int i = 0; i < configurations_.size(); i++) {
+        diff |= configurations_[i].diff(configurations[i]);
+      }
     }
   }
   configurations_ = std::move(configurations);
@@ -775,8 +784,7 @@
     bool has_locale = false;
     if (result->config.locale == 0) {
       if (default_locale_ != 0) {
-        ResTable_config conf;
-        conf.locale = default_locale_;
+        ResTable_config conf = {.locale = default_locale_};
         // Since we know conf has a locale and only a locale, match will tell us if that locale
         // matches
         has_locale = conf.match(config);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index d9ff35b..17a8ba6 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -124,6 +124,9 @@
   // new resource IDs.
   bool SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches = true);
   bool SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets, bool invalidate_caches = true);
+  // This one is an optimization - it skips all calculations for applying the currently set
+  // configuration, expecting a configuration update later with a forced refresh.
+  void PresetApkAssets(ApkAssetsList apk_assets);
 
   const ApkAssetsPtr& GetApkAssets(ApkAssetsCookie cookie) const;
   int GetApkAssetsCount() const {
@@ -156,7 +159,7 @@
 
   // Sets/resets the configuration for this AssetManager. This will cause all
   // caches that are related to the configuration change to be invalidated.
-  void SetConfigurations(std::vector<ResTable_config> configurations);
+  void SetConfigurations(std::vector<ResTable_config> configurations, bool force_refresh = false);
 
   inline const std::vector<ResTable_config>& GetConfigurations() const {
     return configurations_;
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 71f7926..27ea150 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -378,10 +378,17 @@
             break;
         case kAlpha_8_SkColorType:
             formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
-            formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
-            formatInfo.format = GL_RED;
-            formatInfo.type = GL_UNSIGNED_BYTE;
-            formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+            if (formatInfo.isSupported) {
+                formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+                formatInfo.format = GL_RED;
+                formatInfo.type = GL_UNSIGNED_BYTE;
+                formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+            } else {
+                formatInfo.type = GL_UNSIGNED_BYTE;
+                formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+                formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+                formatInfo.format = GL_RGBA;
+            }
             break;
         default:
             ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 8344a86..1854361 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -147,11 +147,7 @@
 }
 
 sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-    if (bitmap.colorType() == kAlpha_8_SkColorType &&
-        !uirenderer::HardwareBitmapUploader::hasAlpha8Support()) {
-        return nullptr;
-    }
+#ifdef __ANDROID__  // Layoutlib does not support hardware acceleration
     return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
 #else
     return Bitmap::allocateHeapBitmap(bitmap.info());
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 2795cfe..f05ea9c 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -336,8 +336,9 @@
      * This method must not be called when the Visualizer is enabled.
      * @param size requested capture size
      * @return {@link #SUCCESS} in case of success,
-     * {@link #ERROR_BAD_VALUE} in case of failure.
-     * @throws IllegalStateException
+     * {@link #ERROR_INVALID_OPERATION} if Visualizer effect enginer not enabled.
+     * @throws IllegalStateException if the effect is not in proper state.
+     * @throws IllegalArgumentException if the size parameter is invalid (out of supported range).
      */
     public int setCaptureSize(int size)
     throws IllegalStateException {
@@ -345,7 +346,13 @@
             if (mState != STATE_INITIALIZED) {
                 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
             }
-            return native_setCaptureSize(size);
+
+            int ret = native_setCaptureSize(size);
+            if (ret == ERROR_BAD_VALUE) {
+                throw(new IllegalArgumentException("setCaptureSize to " + size + " failed"));
+            }
+
+            return ret;
         }
     }
 
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 09c45ea..9ae5c99 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,7 +25,6 @@
 #include <limits.h>
 
 #include <audio_utils/fixedfft.h>
-#include <cutils/bitops.h>
 #include <utils/Thread.h>
 
 #include <android/content/AttributionSourceState.h>
@@ -59,8 +58,8 @@
     status_t status = AudioEffect::set(
             SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe);
     if (status == NO_ERROR || status == ALREADY_EXISTS) {
-        initCaptureSize();
-        initSampleRate();
+        status = initCaptureSize();
+        if (status == NO_ERROR) initSampleRate();
     }
     return status;
 }
@@ -152,9 +151,8 @@
 
 status_t Visualizer::setCaptureSize(uint32_t size)
 {
-    if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
-        size < VISUALIZER_CAPTURE_SIZE_MIN ||
-        popcount(size) != 1) {
+    if (!isCaptureSizeValid(size)) {
+        ALOGE("%s with invalid capture size %u from HAL", __func__, size);
         return BAD_VALUE;
     }
 
@@ -172,7 +170,7 @@
     *((int32_t *)p->data + 1)= size;
     status_t status = setParameter(p);
 
-    ALOGV("setCaptureSize size %d  status %d p->status %d", size, status, p->status);
+    ALOGV("setCaptureSize size %u status %d p->status %d", size, status, p->status);
 
     if (status == NO_ERROR) {
         status = p->status;
@@ -257,8 +255,8 @@
     if ((type != MEASUREMENT_MODE_PEAK_RMS)
             // for peak+RMS measurement, the results are 2 int32_t values
             || (number != 2)) {
-        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
-                        number);
+        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %u",
+              number);
         return BAD_VALUE;
     }
 
@@ -390,7 +388,7 @@
     }
 }
 
-uint32_t Visualizer::initCaptureSize()
+status_t Visualizer::initCaptureSize()
 {
     uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
     effect_param_t *p = (effect_param_t *)buf32;
@@ -405,14 +403,20 @@
     }
 
     uint32_t size = 0;
-    if (status == NO_ERROR) {
-        size = *((int32_t *)p->data + 1);
+    if (status != NO_ERROR) {
+        ALOGE("%s getParameter failed status %d", __func__, status);
+        return status;
     }
+
+    size = *((int32_t *)p->data + 1);
+    if (!isCaptureSizeValid(size)) {
+        ALOGE("%s with invalid capture size %u from HAL", __func__, size);
+        return BAD_VALUE;
+    }
+
     mCaptureSize = size;
-
-    ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
-
-    return size;
+    ALOGV("%s size %u status %d", __func__, mCaptureSize, status);
+    return NO_ERROR;
 }
 
 void Visualizer::initSampleRate()
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index b38c01f..26d58d0 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,8 @@
 #include <media/AudioEffect.h>
 #include <system/audio_effects/effect_visualizer.h>
 #include <utils/Thread.h>
+#include <cstdint>
+#include <cutils/bitops.h>
 #include "android/content/AttributionSourceState.h"
 
 /**
@@ -170,8 +172,12 @@
 
     status_t doFft(uint8_t *fft, uint8_t *waveform);
     void periodicCapture();
-    uint32_t initCaptureSize();
+    status_t initCaptureSize();
     void initSampleRate();
+    static constexpr bool isCaptureSizeValid(uint32_t size) {
+        return size <= VISUALIZER_CAPTURE_SIZE_MAX && size >= VISUALIZER_CAPTURE_SIZE_MIN &&
+                popcount(size) == 1;
+    }
 
     Mutex mCaptureLock;
     uint32_t mCaptureRate = CAPTURE_RATE_DEF;
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index 18320f7..a5b82b3 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -20,6 +20,9 @@
     <style name="Theme.AlertDialogActivity"
         parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
         <item name="alertDialogStyle">@style/AlertDialog</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowAnimationStyle">@null</item>
     </style>
 
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index cd35f67..be480b9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -308,11 +308,8 @@
                 final long token = proto.start(GenerationRegistryProto.BACKING_STORES);
                 final int key = mKeyToBackingStoreMap.keyAt(i);
                 proto.write(BackingStoreProto.KEY, key);
-                try {
-                    proto.write(BackingStoreProto.BACKING_STORE_SIZE,
-                            mKeyToBackingStoreMap.valueAt(i).size());
-                } catch (IOException ignore) {
-                }
+                proto.write(BackingStoreProto.BACKING_STORE_SIZE,
+                        mKeyToBackingStoreMap.valueAt(i).size());
                 proto.write(BackingStoreProto.NUM_CACHED_ENTRIES,
                         mKeyToIndexMapMap.get(key).size());
                 final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
@@ -357,10 +354,7 @@
                 pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString(
                         SettingsState.getTypeFromKey(key)));
                 pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key));
-                try {
-                    pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
-                } catch (IOException ignore) {
-                }
+                pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
                 pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size());
                 final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
                 final MemoryIntArray backingStore = getBackingStoreLocked(key,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f6c794c..7f229fb 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -29,6 +29,13 @@
 }
 
 flag {
+    name: "notification_color_update_logger"
+    namespace: "systemui"
+    description: "Enabled debug logging and dumping of notification color updates."
+    bug: "294347738"
+}
+
+flag {
     name: "notifications_footer_view_refactor"
     namespace: "systemui"
     description: "Enables the refactored version of the footer view in the notification shade "
@@ -462,4 +469,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 55cfcc2..ab55125 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -210,6 +210,32 @@
         }
 
     @Test
+    fun shouldHandleTouchesOnDetach() =
+        testScope.runTest {
+            val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
+
+            // GIVEN view is attached + on the keyguard
+            mController.onViewAttached()
+            captureStatusBarStateListeners()
+            sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+            whenever(mView.setPauseAuth(true)).thenReturn(true)
+            whenever(mView.unpausedAlpha).thenReturn(0)
+
+            // WHEN panelViewExpansion changes to expanded
+            val job = mController.listenForBouncerExpansion(this)
+            keyguardBouncerRepository.setPrimaryShow(true)
+            keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+            runCurrent()
+
+            mController.onViewDetached()
+
+            // THEN UDFPS auth is paused and should not handle touches
+            assertThat(mController.shouldPauseAuth()).isTrue()
+            assertThat(shouldHandleTouches!!).isFalse()
+
+            job.cancel()
+        }
+    @Test
     fun fadeFromDialogSuggestedAlpha() =
         testScope.runTest {
             // GIVEN view is attached and status bar expansion is 1f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 3455050..3104842 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,8 +18,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.content.pm.UserInfo
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -27,21 +25,20 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -85,35 +82,21 @@
 
     @EnableFlags(FLAG_COMMUNAL_HUB)
     @Test
-    fun leftTransitionSceneKey_communalIsEnabled_communal() =
+    fun leftTransitionSceneKey_communalIsAvailable_communal() =
         testScope.runTest {
-            with(kosmos.fakeUserRepository) {
-                setUserInfos(listOf(PRIMARY_USER))
-                setSelectedUserInfo(PRIMARY_USER)
-            }
-            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
-            val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
-            assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
-        }
-
-    @DisableFlags(FLAG_COMMUNAL_HUB)
-    @Test
-    fun leftTransitionSceneKey_communalIsDisabled_null() =
-        testScope.runTest {
-            with(kosmos.fakeUserRepository) {
-                setUserInfos(listOf(PRIMARY_USER))
-                setSelectedUserInfo(PRIMARY_USER)
-            }
-            kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
             val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
             assertThat(leftDestinationSceneKey).isNull()
+
+            kosmos.setCommunalAvailable(true)
+            runCurrent()
+            assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
         }
 
     private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
         return LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = kosmos.deviceEntryInteractor,
-            communalSettingsInteractor = kosmos.communalSettingsInteractor,
+            communalInteractor = kosmos.communalInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
@@ -121,9 +104,4 @@
             notifications = kosmos.notificationsPlaceholderViewModel,
         )
     }
-
-    private companion object {
-        val PRIMARY_USER =
-            UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 7c30c7e..9f89d34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@
 import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
 import com.android.systemui.classifier.domain.interactor.falsingInteractor
 import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -130,7 +130,7 @@
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
     private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
-    private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
+    private val communalInteractor by lazy { kosmos.communalInteractor }
 
     private val transitionState by lazy {
         MutableStateFlow<ObservableTransitionState>(
@@ -155,7 +155,7 @@
         LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
             deviceEntryInteractor = deviceEntryInteractor,
-            communalSettingsInteractor = communalSettingsInteractor,
+            communalInteractor = communalInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index f5603ed..c3e7818 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -132,13 +132,13 @@
     override fun onViewAttached() {
         dialogManager.registerListener(dialogListener)
         dumpManager.registerDumpable(dumpTag, this)
-        udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+        udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
     }
 
     override fun onViewDetached() {
         dialogManager.unregisterListener(dialogListener)
         dumpManager.unregisterDumpable(dumpTag)
-        udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+        udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 018d92e..ec54e4ce 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -378,7 +378,7 @@
         }
     }
 
-    override fun onViewDetached() {
+    public override fun onViewDetached() {
         super.onViewDetached()
         alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
         faceDetectRunning = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index fa18557..9afe8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@
 constructor(
     @Application applicationScope: CoroutineScope,
     deviceEntryInteractor: DeviceEntryInteractor,
-    communalSettingsInteractor: CommunalSettingsInteractor,
+    communalInteractor: CommunalInteractor,
     val longPress: KeyguardLongPressViewModel,
     val notifications: NotificationsPlaceholderViewModel,
 ) {
@@ -56,7 +56,7 @@
 
     /** The key of the scene we should switch to when swiping left. */
     val leftDestinationSceneKey: StateFlow<SceneKey?> =
-        communalSettingsInteractor.isCommunalEnabled
+        communalInteractor.isCommunalAvailable
             .map { available -> if (available) SceneKey.Communal else null }
             .stateIn(
                 scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0e0f152..6155348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar;
 
 import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -42,6 +43,7 @@
 import com.android.systemui.flags.RefactorFlag;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -187,8 +189,8 @@
 
     @Override
     public String toString() {
-        return "NotificationShelf"
-                + "(hideBackground=" + mHideBackground
+        return super.toString()
+                + " (hideBackground=" + mHideBackground
                 + " notGoneIndex=" + mNotGoneIndex
                 + " hasItemsInStableShelf=" + mHasItemsInStableShelf
                 + " interactive=" + mInteractive
@@ -368,6 +370,17 @@
                 && isYInView(localY, slop, top, bottom);
     }
 
+    @Override
+    public void updateBackgroundColors() {
+        super.updateBackgroundColors();
+        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+        if (colorUpdateLogger != null) {
+            colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
+                    "normalBgColor=" + hexColorString(getNormalBgColor())
+                            + " background=" + mBackgroundNormal.toDumpString());
+        }
+    }
+
     /**
      * Update the shelf appearance based on the other notifications around it. This transforms
      * the icons from the notification area into the shelf.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
new file mode 100644
index 0000000..c8f996a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.systemui.statusbar.notification
+
+import android.icu.text.SimpleDateFormat
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationColorUpdateLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.util.Compile
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import com.android.systemui.util.withIncreasedIndent
+import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.SortedSet
+import java.util.TreeSet
+import javax.inject.Inject
+
+@SysUISingleton
+class ColorUpdateLogger
+@Inject
+constructor(
+    val featureFlags: FeatureFlagsClassic,
+    dumpManager: DumpManager,
+) : Dumpable {
+
+    inline val isEnabled
+        get() = Compile.IS_DEBUG && notificationColorUpdateLogger()
+    private val frames: MutableList<Frame> = mutableListOf()
+
+    init {
+        dumpManager.registerDumpable(this)
+        if (isEnabled) {
+            instance = this
+        }
+    }
+
+    @JvmOverloads
+    fun logTriggerEvent(@CompileTimeConstant type: String, extra: String? = null) {
+        if (!isEnabled) return
+        val event = Event(type = type, extraValue = extra)
+        val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true
+        if (!didAppend) {
+            frames.add(Frame(event))
+            if (frames.size > maxFrames) frames.removeFirst()
+        }
+    }
+
+    @JvmOverloads
+    fun logEvent(@CompileTimeConstant type: String, extra: String? = null) {
+        if (!isEnabled) return
+        val frame = frames.lastOrNull() ?: return
+        frame.events.add(Event(type = type, extraValue = extra))
+        frame.trim()
+    }
+
+    @JvmOverloads
+    fun logNotificationEvent(
+        @CompileTimeConstant type: String,
+        key: String,
+        extra: String? = null
+    ) {
+        if (!isEnabled) return
+        val frame = frames.lastOrNull() ?: return
+        frame.events.add(Event(type = type, extraValue = extra, notificationKey = key))
+        frame.trim()
+    }
+
+    override fun dump(pwOrig: PrintWriter, args: Array<out String>) {
+        val pw = pwOrig.asIndenting()
+        pw.println("enabled: $isEnabled")
+        pw.printCollection("frames", frames) { it.dump(pw) }
+    }
+
+    private class Frame(event: Event) {
+        val startTime: Long = event.time
+        val events: MutableList<Event> = mutableListOf(event)
+        val triggers: SortedSet<String> = TreeSet<String>().apply { add(event.type) }
+        var trimmedEvents: Int = 0
+
+        fun tryAddTrigger(newEvent: Event): Boolean {
+            if (newEvent.time < startTime) return false
+            if (newEvent.time - startTime > triggerStartsNewFrameAge) return false
+            if (newEvent.type in triggers) return false
+            triggers.add(newEvent.type)
+            events.add(newEvent)
+            trim()
+            return true
+        }
+
+        fun trim() {
+            if (events.size > maxEventsPerFrame) {
+                events.removeFirst()
+                trimmedEvents++
+            }
+        }
+
+        fun dump(pw: IndentingPrintWriter) {
+            pw.println("Frame")
+            pw.withIncreasedIndent {
+                pw.println("startTime: ${timeString(startTime)}")
+                pw.printCollection("triggers", triggers)
+                pw.println("trimmedEvents: $trimmedEvents")
+                pw.printCollection("events", events) { it.dump(pw) }
+            }
+        }
+    }
+
+    private class Event(
+        @CompileTimeConstant val type: String,
+        val extraValue: String? = null,
+        val notificationKey: String? = null,
+    ) {
+        val time: Long = System.currentTimeMillis()
+
+        fun dump(pw: IndentingPrintWriter) {
+            pw.append(timeString(time)).append(": ").append(type)
+            extraValue?.let { pw.append(" ").append(it) }
+            notificationKey?.let { pw.append(" ---- ").append(logKey(it)) }
+            pw.println()
+        }
+    }
+
+    private companion object {
+        @JvmStatic
+        var instance: ColorUpdateLogger? = null
+            private set
+        private const val maxFrames = 5
+        private const val maxEventsPerFrame = 250
+        private const val triggerStartsNewFrameAge = 5000
+
+        private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+        private fun timeString(time: Long): String = dateFormat.format(time)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 3809ea0..b8a9594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -23,6 +23,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
@@ -41,7 +42,8 @@
     private val mConfigurationController: ConfigurationController,
     private val mLockscreenUserManager: NotificationLockscreenUserManager,
     private val mGutsManager: NotificationGutsManager,
-    private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+    private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val colorUpdateLogger: ColorUpdateLogger,
 ) : Coordinator, ConfigurationController.ConfigurationListener {
 
     private var mIsSwitchingUser = false
@@ -51,11 +53,13 @@
 
     private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
         override fun onUserSwitching(userId: Int) {
+            colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
             log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
             mIsSwitchingUser = true
         }
 
         override fun onUserSwitchComplete(userId: Int) {
+            colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitchComplete()")
             log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
             mIsSwitchingUser = false
             applyChangesOnUserSwitched()
@@ -64,6 +68,7 @@
 
     private val mUserChangedListener = object : UserChangedListener {
         override fun onUserChanged(userId: Int) {
+            colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
             log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
             applyChangesOnUserSwitched()
         }
@@ -77,6 +82,7 @@
     }
 
     override fun onDensityOrFontScaleChanged() {
+        colorUpdateLogger.logTriggerEvent("VCC.onDensityOrFontScaleChanged()")
         log {
             val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
             "ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
@@ -93,6 +99,7 @@
     }
 
     override fun onUiModeChanged() {
+        colorUpdateLogger.logTriggerEvent("VCC.onUiModeChanged()")
         log {
             val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
             "ViewConfigCoordinator.onUiModeChanged()" +
@@ -107,10 +114,12 @@
     }
 
     override fun onThemeChanged() {
+        colorUpdateLogger.logTriggerEvent("VCC.onThemeChanged()")
         onDensityOrFontScaleChanged()
     }
 
     private fun applyChangesOnUserSwitched() {
+        colorUpdateLogger.logEvent("VCC.applyChangesOnUserSwitched()")
         if (mReinflateNotificationsOnUserSwitched) {
             updateNotificationsOnDensityOrFontScaleChanged()
             mReinflateNotificationsOnUserSwitched = false
@@ -122,6 +131,8 @@
     }
 
     private fun updateNotificationsOnUiModeChanged() {
+        colorUpdateLogger.logEvent("VCC.updateNotificationsOnUiModeChanged()",
+                "mode=" + mConfigurationController.nightModeName)
         log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
         traceSection("updateNotifOnUiModeChanged") {
             mPipeline?.allNotifs?.forEach { entry ->
@@ -131,6 +142,7 @@
     }
 
     private fun updateNotificationsOnDensityOrFontScaleChanged() {
+        colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
         mPipeline?.allNotifs?.forEach { entry ->
             entry.onDensityOrFontScaleChanged()
             val exposedGuts = entry.areGutsExposed()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 16f18a3..f792898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -19,6 +19,7 @@
 import static android.graphics.PorterDuff.Mode.SRC_ATOP;
 
 import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
 
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -40,11 +41,13 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.row.FooterViewButton;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.util.DrawableDumpKt;
 import com.android.systemui.util.DumpUtilsKt;
 
 import java.io.PrintWriter;
@@ -239,6 +242,10 @@
 
     @Override
     protected void onFinishInflate() {
+        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+        if (colorUpdateLogger != null) {
+            colorUpdateLogger.logTriggerEvent("Footer.onFinishInflate()");
+        }
         super.onFinishInflate();
         mClearAllButton = (FooterViewButton) findSecondaryView();
         mManageOrHistoryButton = findViewById(R.id.manage_text);
@@ -348,6 +355,10 @@
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
+        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+        if (colorUpdateLogger != null) {
+            colorUpdateLogger.logTriggerEvent("Footer.onConfigurationChanged()");
+        }
         super.onConfigurationChanged(newConfig);
         updateColors();
         if (!FooterViewRefactor.isEnabled()) {
@@ -365,14 +376,17 @@
                 com.android.internal.R.attr.materialColorOnSurface);
         final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
         final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+        final @ColorInt int scHigh;
         if (!notificationBackgroundTintOptimization()) {
-            final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
+            scHigh = Utils.getColorAttrDefaultColor(mContext,
                     com.android.internal.R.attr.materialColorSurfaceContainerHigh);
             if (scHigh != 0) {
                 final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
                 clearAllBg.setColorFilter(bgColorFilter);
                 manageBg.setColorFilter(bgColorFilter);
             }
+        } else {
+            scHigh = 0;
         }
         mClearAllButton.setBackground(clearAllBg);
         mClearAllButton.setTextColor(onSurface);
@@ -380,6 +394,13 @@
         mManageOrHistoryButton.setTextColor(onSurface);
         mSeenNotifsFooterTextView.setTextColor(onSurface);
         mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+        if (colorUpdateLogger != null) {
+            colorUpdateLogger.logEvent("Footer.updateColors()",
+                    "textColor(onSurface)=" + hexColorString(onSurface)
+                            + " backgroundTint(surfaceContainerHigh)=" + hexColorString(scHigh)
+                            + " background=" + DrawableDumpKt.dumpToString(manageBg));
+        }
     }
 
     private void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index fca527f..7358034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -88,7 +88,7 @@
     private boolean mActivated;
 
     private Interpolator mCurrentAppearInterpolator;
-    NotificationBackgroundView mBackgroundNormal;
+    protected NotificationBackgroundView mBackgroundNormal;
     private float mAnimationTranslationY;
     private boolean mDrawingAppearAnimation;
     private ValueAnimator mAppearAnimator;
@@ -142,6 +142,10 @@
         updateBackgroundTint();
     }
 
+    protected int getNormalBgColor() {
+        return mNormalColor;
+    }
+
     /**
      * @param width The actual width to apply to the background view.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index cc91ed3..d828ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -21,6 +21,7 @@
 
 import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -85,6 +86,7 @@
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationFadeAware;
@@ -172,6 +174,7 @@
     private Optional<BubblesManager> mBubblesManagerOptional;
     private MetricsLogger mMetricsLogger;
     private NotificationChildrenContainerLogger mChildrenContainerLogger;
+    private ColorUpdateLogger mColorUpdateLogger;
     private NotificationDismissibilityProvider mDismissibilityProvider;
     private FeatureFlags mFeatureFlags;
     private int mIconTransformContentShift;
@@ -445,6 +448,7 @@
 
     /**
      * Sets animations running in the layouts of this row, including public, private, and children.
+     *
      * @param running whether the animations should be started running or stopped.
      */
     public void setAnimationRunning(boolean running) {
@@ -611,6 +615,12 @@
 
     private void updateBackgroundColorsOfSelf() {
         super.updateBackgroundColors();
+        if (mColorUpdateLogger.isEnabled()) {
+            mColorUpdateLogger.logNotificationEvent("ENR.updateBackgroundColorsOfSelf()",
+                    mLoggingKey,
+                    "normalBgColor=" + hexColorString(getNormalBgColor())
+                            + " background=" + mBackgroundNormal.toDumpString());
+        }
     }
 
     @Override
@@ -1389,7 +1399,7 @@
     }
 
     public void setContentBackground(int customBackgroundColor, boolean animate,
-                                     NotificationContentView notificationContentView) {
+            NotificationContentView notificationContentView) {
         if (getShowingLayout() == notificationContentView) {
             setTintColor(customBackgroundColor, animate);
         }
@@ -1458,7 +1468,7 @@
     }
 
     /**
-     * @return  if this entry should be kept in its parent during removal.
+     * @return if this entry should be kept in its parent during removal.
      */
     public boolean keepInParentForDismissAnimation() {
         return mKeepInParentForDismissAnimation;
@@ -1769,6 +1779,7 @@
             NotificationDismissibilityProvider dismissibilityProvider,
             MetricsLogger metricsLogger,
             NotificationChildrenContainerLogger childrenContainerLogger,
+            ColorUpdateLogger colorUpdateLogger,
             SmartReplyConstants smartReplyConstants,
             SmartReplyController smartReplyController,
             FeatureFlags featureFlags,
@@ -1807,6 +1818,7 @@
         mNotificationGutsManager = gutsManager;
         mMetricsLogger = metricsLogger;
         mChildrenContainerLogger = childrenContainerLogger;
+        mColorUpdateLogger = colorUpdateLogger;
         mDismissibilityProvider = dismissibilityProvider;
         mFeatureFlags = featureFlags;
     }
@@ -2265,7 +2277,7 @@
     }
 
     public Animator getTranslateViewAnimator(final float leftTarget,
-                                             AnimatorUpdateListener listener) {
+            AnimatorUpdateListener listener) {
         if (mTranslateAnim != null) {
             mTranslateAnim.cancel();
         }
@@ -2664,6 +2676,7 @@
             return getCollapsedHeight();
         }
     }
+
     /**
      * @return {@code true} if the notification can show it's heads up layout. This is mostly true
      * except for legacy use cases.
@@ -2833,7 +2846,7 @@
 
     @Override
     public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
-                                 long duration) {
+            long duration) {
         if (getVisibility() == GONE) {
             // If we are GONE, the hideSensitive parameter will not be calculated and always be
             // false, which is incorrect, let's wait until a real call comes in later.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 5614f3a..e59829b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -41,6 +41,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FeedbackIcon;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
@@ -86,6 +87,7 @@
     private final SystemClock mClock;
     private final String mAppName;
     private final String mNotificationKey;
+    private final ColorUpdateLogger mColorUpdateLogger;
     private final KeyguardBypassController mKeyguardBypassController;
     private final GroupMembershipManager mGroupMembershipManager;
     private final GroupExpansionManager mGroupExpansionManager;
@@ -200,6 +202,7 @@
             ActivatableNotificationViewController activatableNotificationViewController,
             RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
             MetricsLogger metricsLogger,
+            ColorUpdateLogger colorUpdateLogger,
             NotificationRowLogger logBufferLogger,
             NotificationChildrenContainerLogger childrenContainerLogger,
             NotificationListContainer listContainer,
@@ -256,6 +259,7 @@
         mDragController = dragController;
         mMetricsLogger = metricsLogger;
         mChildrenContainerLogger = childrenContainerLogger;
+        mColorUpdateLogger = colorUpdateLogger;
         mLogBufferLogger = logBufferLogger;
         mSmartReplyConstants = smartReplyConstants;
         mSmartReplyController = smartReplyController;
@@ -290,6 +294,7 @@
                 mDismissibilityProvider,
                 mMetricsLogger,
                 mChildrenContainerLogger,
+                mColorUpdateLogger,
                 mSmartReplyConstants,
                 mSmartReplyController,
                 mFeatureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ec8e5d7..ea9df9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.Flags.notificationColorUpdateLogger;
+
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -54,8 +56,8 @@
 public abstract class ExpandableView extends FrameLayout implements Dumpable, Roundable {
     private static final String TAG = "ExpandableView";
     /** whether the dump() for this class should include verbose details */
-    protected static final boolean DUMP_VERBOSE =
-            Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
+    protected static final boolean DUMP_VERBOSE = Compile.IS_DEBUG
+            && (Log.isLoggable(TAG, Log.VERBOSE) || notificationColorUpdateLogger());
 
     private RoundableState mRoundableState = null;
     protected OnHeightChangedListener mOnHeightChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 7ea9b14..ed3a38d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,6 +36,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.res.R;
+import com.android.systemui.util.DrawableDumpKt;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -333,6 +334,16 @@
         pw.println("mActualHeight: " + mActualHeight);
         pw.println("mTintColor: " + hexColorString(mTintColor));
         pw.println("mRippleColor: " + hexColorString(mRippleColor));
-        pw.println("mBackground: " + mBackground);
+        pw.println("mBackground: " + DrawableDumpKt.dumpToString(mBackground));
+    }
+
+    /** create a concise dump of this view's colors */
+    public String toDumpString() {
+        return "<NotificationBackgroundView"
+                + " tintColor=" + hexColorString(mTintColor)
+                + " rippleColor=" + hexColorString(mRippleColor)
+                + " bgColor=" + DrawableDumpKt.getSolidColor(mBackground)
+                + ">";
+
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 933a780..7925a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -99,6 +99,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
@@ -122,6 +123,7 @@
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.ColorUtilKt;
 import com.android.systemui.util.DumpUtilsKt;
 
 import com.google.errorprone.annotations.CompileTimeConstant;
@@ -805,8 +807,8 @@
         updateBackgroundDimming();
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
-            if (child instanceof ActivatableNotificationView) {
-                ((ActivatableNotificationView) child).updateBackgroundColors();
+            if (child instanceof ActivatableNotificationView activatableView) {
+                activatableView.updateBackgroundColors();
             }
         }
     }
@@ -4595,6 +4597,13 @@
         final @ColorInt int onSurfaceVariant = Utils.getColorAttrDefaultColor(
                 mContext, com.android.internal.R.attr.materialColorOnSurfaceVariant);
 
+        ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+        if (colorUpdateLogger != null) {
+            colorUpdateLogger.logEvent("NSSL.updateDecorViews()",
+                    "onSurface=" + ColorUtilKt.hexColorString(onSurface)
+                            + " onSurfaceVariant=" + ColorUtilKt.hexColorString(onSurfaceVariant));
+        }
+
         mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
 
         if (mFooterView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 78e6a79..d2ff266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -96,6 +96,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -177,6 +178,7 @@
     private final ConfigurationController mConfigurationController;
     private final ZenModeController mZenModeController;
     private final MetricsLogger mMetricsLogger;
+    private final ColorUpdateLogger mColorUpdateLogger;
 
     private final DumpManager mDumpManager;
     private final FalsingCollector mFalsingCollector;
@@ -239,6 +241,7 @@
             new View.OnAttachStateChangeListener() {
                 @Override
                 public void onViewAttachedToWindow(View v) {
+                    mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
                     mConfigurationController.addCallback(mConfigurationListener);
                     if (!FooterViewRefactor.isEnabled()) {
                         mZenModeController.addCallback(mZenModeControllerCallback);
@@ -254,6 +257,7 @@
 
                 @Override
                 public void onViewDetachedFromWindow(View v) {
+                    mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
                     mConfigurationController.removeCallback(mConfigurationListener);
                     if (!FooterViewRefactor.isEnabled()) {
                         mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -332,12 +336,16 @@
 
         @Override
         public void onUiModeChanged() {
+            mColorUpdateLogger.logTriggerEvent("NSSLC.onUiModeChanged()",
+                    "mode=" + mConfigurationController.getNightModeName());
             mView.updateBgColor();
             mView.updateDecorViews();
         }
 
         @Override
         public void onThemeChanged() {
+            mColorUpdateLogger.logTriggerEvent("NSSLC.onThemeChanged()",
+                    "mode=" + mConfigurationController.getNightModeName());
             mView.updateCornerRadius();
             mView.updateBgColor();
             mView.updateDecorViews();
@@ -719,6 +727,7 @@
             ZenModeController zenModeController,
             NotificationLockscreenUserManager lockscreenUserManager,
             MetricsLogger metricsLogger,
+            ColorUpdateLogger colorUpdateLogger,
             DumpManager dumpManager,
             FalsingCollector falsingCollector,
             FalsingManager falsingManager,
@@ -773,6 +782,7 @@
         mZenModeController = zenModeController;
         mLockscreenUserManager = lockscreenUserManager;
         mMetricsLogger = metricsLogger;
+        mColorUpdateLogger = colorUpdateLogger;
         mDumpManager = dumpManager;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mFalsingCollector = falsingCollector;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 6e8ad2e..dea9416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -159,6 +159,15 @@
     override fun isLayoutRtl(): Boolean {
         return layoutDirection == LAYOUT_DIRECTION_RTL
     }
+
+    override fun getNightModeName(): String {
+        return when (uiMode and Configuration.UI_MODE_NIGHT_MASK) {
+            Configuration.UI_MODE_NIGHT_YES -> "night"
+            Configuration.UI_MODE_NIGHT_NO -> "day"
+            Configuration.UI_MODE_NIGHT_UNDEFINED -> "undefined"
+            else -> "err"
+        }
+    }
 }
 
 // This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index b2ef818..cec77c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,9 @@
     /** Query the current configuration's layout direction */
     boolean isLayoutRtl();
 
+    /** Logging only; Query the current configuration's night mode name */
+    String getNightModeName();
+
     interface ConfigurationListener {
         default void onConfigChanged(Configuration newConfig) {}
         default void onDensityOrFontScaleChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
new file mode 100644
index 0000000..0c079a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.systemui.util
+
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.BlendModeColorFilter
+import android.graphics.ColorFilter
+import android.graphics.LightingColorFilter
+import android.graphics.PorterDuffColorFilter
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RippleDrawable
+import android.util.Log
+
+fun dumpToString(drawable: Drawable?): String =
+    if (Compile.IS_DEBUG) StringBuilder().appendDrawable(drawable).toString()
+    else drawable.toString()
+
+fun getSolidColor(drawable: Drawable?): String =
+    if (Compile.IS_DEBUG) hexColorString(getSolidColors(drawable)?.defaultColor)
+    else if (drawable == null) "null" else "?"
+
+private fun getSolidColors(drawable: Drawable?): ColorStateList? {
+    return when (drawable) {
+        is GradientDrawable -> {
+            return drawable.getStateField<ColorStateList>("mSolidColors")
+        }
+        is LayerDrawable -> {
+            for (iLayer in 0 until drawable.numberOfLayers) {
+                getSolidColors(drawable.getDrawable(iLayer))?.let {
+                    return it
+                }
+            }
+            null
+        }
+        is DrawableWrapper -> {
+            return getSolidColors(drawable.drawable)
+        }
+        else -> null
+    }
+}
+
+private fun StringBuilder.appendDrawable(drawable: Drawable?): StringBuilder {
+    if (drawable == null) {
+        append("null")
+        return this
+    }
+    append("<")
+    append(drawable.javaClass.simpleName)
+
+    drawable.getStateField<ColorStateList>("mTint", fieldRequired = false)?.let {
+        append(" tint=")
+        appendColors(it)
+        append(" blendMode=")
+        append(drawable.getStateField<BlendMode>("mBlendMode"))
+    }
+    drawable.colorFilter
+        ?.takeUnless { drawable is DrawableWrapper }
+        ?.let {
+            append(" colorFilter=")
+            appendColorFilter(it)
+        }
+    when (drawable) {
+        is DrawableWrapper -> {
+            append(" wrapped=")
+            appendDrawable(drawable.drawable)
+        }
+        is LayerDrawable -> {
+            if (drawable is RippleDrawable) {
+                drawable.getStateField<ColorStateList>("mColor")?.let {
+                    append(" color=")
+                    appendColors(it)
+                }
+                drawable.effectColor?.let {
+                    append(" effectColor=")
+                    appendColors(it)
+                }
+            }
+            append(" layers=[")
+            for (iLayer in 0 until drawable.numberOfLayers) {
+                if (iLayer != 0) append(", ")
+                appendDrawable(drawable.getDrawable(iLayer))
+            }
+            append("]")
+        }
+        is GradientDrawable -> {
+            drawable
+                .getStateField<Int>("mShape")
+                ?.takeIf { it != 0 }
+                ?.let {
+                    append(" shape=")
+                    append(it)
+                }
+            drawable.getStateField<ColorStateList>("mSolidColors")?.let {
+                append(" solidColors=")
+                appendColors(it)
+            }
+            drawable.getStateField<ColorStateList>("mStrokeColors")?.let {
+                append(" strokeColors=")
+                appendColors(it)
+            }
+            drawable.colors?.let {
+                append(" gradientColors=[")
+                it.forEachIndexed { iColor, color ->
+                    if (iColor != 0) append(", ")
+                    append(hexColorString(color))
+                }
+                append("]")
+            }
+        }
+    }
+    append(">")
+    return this
+}
+
+private inline fun <reified T> Drawable.getStateField(
+    name: String,
+    fieldRequired: Boolean = true
+): T? {
+    val state = this.constantState ?: return null
+    val clazz = state.javaClass
+    return try {
+        val field = clazz.getDeclaredField(name)
+        field.isAccessible = true
+        field.get(state) as T?
+    } catch (ex: Exception) {
+        if (fieldRequired) {
+            Log.w(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName}", ex)
+        } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName} ($ex)")
+        }
+        null
+    }
+}
+
+private fun Appendable.appendColors(colorStateList: ColorStateList?) {
+    if (colorStateList == null) {
+        append("null")
+        return
+    }
+    val colors = colorStateList.colors
+    if (colors.size == 1) {
+        append(hexColorString(colors[0]))
+        return
+    }
+    append("<ColorStateList size=")
+    append(colors.size.toString())
+    append(" default=")
+    append(hexColorString(colorStateList.defaultColor))
+    append(">")
+}
+
+private fun Appendable.appendColorFilter(colorFilter: ColorFilter?) {
+    if (colorFilter == null) {
+        append("null")
+        return
+    }
+    append("<")
+    append(colorFilter.javaClass.simpleName)
+    when (colorFilter) {
+        is PorterDuffColorFilter -> {
+            append(" color=")
+            append(hexColorString(colorFilter.color))
+            append(" mode=")
+            append(colorFilter.mode.toString())
+        }
+        is BlendModeColorFilter -> {
+            append(" color=")
+            append(hexColorString(colorFilter.color))
+            append(" mode=")
+            append(colorFilter.mode.toString())
+        }
+        is LightingColorFilter -> {
+            append(" multiply=")
+            append(hexColorString(colorFilter.colorMultiply))
+            append(" add=")
+            append(hexColorString(colorFilter.colorAdd))
+        }
+        else -> append(" unhandled")
+    }
+    append(">")
+}
+
+private const val TAG = "DrawableDump"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 992658a..db455cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -96,6 +96,7 @@
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewConfigurator;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
@@ -354,6 +355,7 @@
     protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     protected KeyguardClockInteractor mKeyguardClockInteractor;
     protected FakeKeyguardRepository mFakeKeyguardRepository;
+    protected FakeKeyguardClockRepository mFakeKeyguardClockRepository;
     protected KeyguardInteractor mKeyguardInteractor;
     protected ShadeAnimationInteractor mShadeAnimationInteractor;
     protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -399,6 +401,8 @@
                 KeyguardInteractorFactory.create();
         mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
         mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
+        mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
+        mKeyguardClockInteractor = new KeyguardClockInteractor(mFakeKeyguardClockRepository);
         mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
         mShadeRepository = new FakeShadeRepository();
         mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 0830191..b1d2ea21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -57,6 +58,7 @@
     private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
     private val gutsManager: NotificationGutsManager = mock()
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+    private val colorUpdateLogger: ColorUpdateLogger = mock()
 
     @Before
     fun setUp() {
@@ -66,7 +68,9 @@
             configurationController,
             lockscreenUserManager,
             gutsManager,
-            keyguardUpdateMonitor)
+            keyguardUpdateMonitor,
+            colorUpdateLogger,
+        )
         coordinator.attach(pipeline)
         userChangedListener = withArgCaptor {
             verify(lockscreenUserManager).addUserChangedListener(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 8ac2a33..210b1a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.SbnBuilder
 import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -82,6 +83,7 @@
     private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
     private val metricsLogger: MetricsLogger = mock()
     private val logBufferLogger = NotificationRowLogger(logcatLogBuffer(), logcatLogBuffer())
+    private val colorUpdateLogger: ColorUpdateLogger = mock()
     private val listContainer: NotificationListContainer = mock()
     private val childrenContainer: NotificationChildrenContainer = mock()
     private val smartReplyConstants: SmartReplyConstants = mock()
@@ -117,6 +119,7 @@
                 activableNotificationViewController,
                 rivSubComponentFactory,
                 metricsLogger,
+                colorUpdateLogger,
                 logBufferLogger,
                 NotificationChildrenContainerLogger(logcatLogBuffer()),
                 listContainer,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c717991..e78081f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -65,6 +65,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -607,6 +608,7 @@
                 mDismissibilityProvider,
                 mock(MetricsLogger.class),
                 new NotificationChildrenContainerLogger(logcatLogBuffer()),
+                mock(ColorUpdateLogger.class),
                 mock(SmartReplyConstants.class),
                 mock(SmartReplyController.class),
                 mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 354f3f6..f2ef4e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -78,6 +78,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -148,6 +149,7 @@
     @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
     @Mock private MetricsLogger mMetricsLogger;
+    @Mock private ColorUpdateLogger mColorUpdateLogger;
     @Mock private DumpManager mDumpManager;
     @Mock(answer = Answers.RETURNS_SELF)
     private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -1007,6 +1009,7 @@
                 mZenModeController,
                 mNotificationLockscreenUserManager,
                 mMetricsLogger,
+                mColorUpdateLogger,
                 mDumpManager,
                 new FalsingCollectorFake(),
                 new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index f7e9a11..566fc25 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -21,12 +21,16 @@
 import com.android.systemui.communal.data.repository.communalRepository
 import com.android.systemui.communal.data.repository.communalWidgetRepository
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.smartspace.data.repository.smartspaceRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.communalInteractor by Fixture {
@@ -47,3 +51,14 @@
 }
 
 val Kosmos.editWidgetsActivityStarter by Fixture<EditWidgetsActivityStarter> { mock() }
+
+suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
+    fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, available)
+    if (available) {
+        fakeUserRepository.asMainUser()
+        with(fakeKeyguardRepository) {
+            setIsEncryptedOrLockdown(false)
+            setKeyguardShowing(true)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index c51de33..46a1053 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -43,6 +43,7 @@
     }
 
     override fun isLayoutRtl(): Boolean = isRtl
+    override fun getNightModeName(): String = "undefined"
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 1124425..931a59d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -39,13 +39,19 @@
         // User id to represent a non system (human) user id. We presume this is the main user.
         private const val MAIN_USER_ID = 10
 
-        private val DEFAULT_SELECTED_USER = 0
+        private const val DEFAULT_SELECTED_USER = 0
         private val DEFAULT_SELECTED_USER_INFO =
             UserInfo(
                 /* id= */ DEFAULT_SELECTED_USER,
                 /* name= */ "default selected user",
                 /* flags= */ 0,
             )
+        private val MAIN_USER =
+            UserInfo(
+                /* id= */ MAIN_USER_ID,
+                /* name= */ "main user",
+                /* flags= */ UserInfo.FLAG_MAIN,
+            )
     }
 
     private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -113,6 +119,13 @@
         yield()
     }
 
+    /** Makes the current user [MAIN_USER]. */
+    suspend fun asMainUser(): UserInfo {
+        setUserInfos(listOf(MAIN_USER))
+        setSelectedUserInfo(MAIN_USER)
+        return MAIN_USER
+    }
+
     suspend fun setSettings(settings: UserSwitcherSettingsModel) {
         _userSwitcherSettings.value = settings
         yield()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index 516eb6e..111c40d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -38,4 +38,9 @@
     public boolean isLayoutRtl() {
         return false;
     }
+
+    @Override
+    public String getNightModeName() {
+        return "undefined";
+    }
 }
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
index 6eec0de..3ffd2e1 100644
--- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -34,6 +34,7 @@
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.InputDevice;
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RoundedCorner;
@@ -335,7 +336,15 @@
 
         final int unicodeChar = event.getUnicodeChar();
         if (unicodeChar != 0) {
-            return new String(Character.toChars(unicodeChar));
+            if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+                // Show combining character
+                final int combiningChar = KeyCharacterMap.getCombiningChar(
+                        unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK);
+                // Return the Unicode dotted circle as part of the label as it is used is used to
+                // illustrate the effect of a combining marks
+                return "\u25cc" + String.valueOf((char) combiningChar);
+            }
+            return String.valueOf((char) unicodeChar);
         }
 
         final var label = KeyEvent.keyCodeToString(event.getKeyCode());
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 133fc8f..59d3d17 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -143,7 +143,8 @@
     // Magic number to mark block device as adhering to the format consumed by this service
     private static final int PARTITION_TYPE_MARKER = 0x19901873;
     /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
-    private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
+    @VisibleForTesting
+    static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
     /** Maximum size of the FRP credential handle that can be stored. */
     @VisibleForTesting
     static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
@@ -158,7 +159,8 @@
     /**
      * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
      */
-    private static final int TEST_MODE_RESERVED_SIZE = 10000;
+    @VisibleForTesting
+    static final int TEST_MODE_RESERVED_SIZE = 10000;
     /** Maximum size of the Test Harness Mode data that can be stored. */
     @VisibleForTesting
     static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
@@ -393,7 +395,8 @@
         return totalDataSize;
     }
 
-    private long getBlockDeviceSize() {
+    @VisibleForTesting
+    long getBlockDeviceSize() {
         synchronized (mLock) {
             if (mBlockDeviceSize == -1) {
                 if (mIsFileBacked) {
@@ -553,26 +556,33 @@
             channel.write(buf);
             channel.force(true);
 
-            // 3. skip the test mode data and leave it unformatted.
+            // 3. Write the default FRP secret (all zeros).
+            if (mFrpEnforced) {
+                Slog.i(TAG, "Writing FRP secret magic");
+                channel.write(ByteBuffer.wrap(FRP_SECRET_MAGIC));
+
+                Slog.i(TAG, "Writing default FRP secret");
+                channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
+                channel.force(true);
+
+                mFrpActive = false;
+            }
+
+            // 4. skip the test mode data and leave it unformatted.
             //    This is for a feature that enables testing.
             channel.position(channel.position() + TEST_MODE_RESERVED_SIZE);
 
-            // 4. wipe the FRP_CREDENTIAL explicitly
+            // 5. wipe the FRP_CREDENTIAL explicitly
             buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
             channel.write(buf);
             channel.force(true);
 
-            // 5. set unlock = 0 because it's a formatPartitionLocked
+            // 6. set unlock = 0 because it's a formatPartitionLocked
             buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
             buf.put((byte)0);
             buf.flip();
             channel.write(buf);
             channel.force(true);
-
-            // 6. Write the default FRP secret (all zeros).
-            if (mFrpEnforced) {
-                writeFrpMagicAndDefaultSecret();
-            }
         } catch (IOException e) {
             Slog.e(TAG, "failed to format block", e);
             return;
@@ -616,7 +626,7 @@
             // version.  If so, we deactivate FRP and set the secret to the default value.
             if (isUpgradingFromPreVRelease()) {
                 Slog.w(TAG, "Upgrading from Android 14 or lower, defaulting FRP secret");
-                writeFrpMagicAndDefaultSecret();
+                writeFrpMagicAndDefaultSecretLocked();
                 mFrpActive = false;
                 return true;
             }
@@ -630,7 +640,7 @@
         try {
             return deactivateFrp(Files.readAllBytes(Paths.get(frpSecretFile)));
         } catch (IOException e) {
-            Slog.w(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
+            Slog.i(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
                     + e.getClass().getSimpleName());
             return false;
         }
@@ -653,7 +663,8 @@
     }
 
     /**
-     * Write the provided secret to the FRP secret file in /data and to the /persist partition.
+     * Write the provided secret to the FRP secret file in /data and to the persistent data block
+     * partition.
      *
      * Writing is a three-step process, to ensure that we can recover from a crash at any point.
      */
@@ -713,7 +724,7 @@
         synchronized (mLock) {
             if (!hasFrpSecretMagic()) {
                 Slog.i(TAG, "No FRP secret magic, system must have been upgraded.");
-                writeFrpMagicAndDefaultSecret();
+                writeFrpMagicAndDefaultSecretLocked();
             }
         }
 
@@ -735,11 +746,9 @@
         }
     }
 
-    private void writeFrpMagicAndDefaultSecret() {
+    private void writeFrpMagicAndDefaultSecretLocked() {
         try (FileChannel channel = getBlockOutputChannelIgnoringFrp()) {
             synchronized (mLock) {
-                // Write secret first in case we crash between the writes, causing the first write
-                // to be synced but the second to be lost.
                 Slog.i(TAG, "Writing default FRP secret");
                 channel.position(getFrpSecretDataOffset());
                 channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
@@ -755,6 +764,7 @@
         } catch (IOException e) {
             Slog.e(TAG, "Failed to write FRP magic and default secret", e);
         }
+        computeAndWriteDigestLocked();
     }
 
     @VisibleForTesting
@@ -879,7 +889,7 @@
                 if (printSecret) {
                     try {
                         pw.println("FRP secret in " + frpSecretFile + ": " + HexFormat.of()
-                                .formatHex(Files.readAllBytes(Paths.get(mFrpSecretFile))));
+                                .formatHex(Files.readAllBytes(Paths.get(frpSecretFile))));
                     } catch (IOException e) {
                         Slog.e(TAG, "Failed to read " + frpSecretFile, e);
                     }
@@ -1230,6 +1240,7 @@
 
         @Override
         public boolean setFactoryResetProtectionSecret(byte[] secret) {
+            enforceConfigureFrpPermission();
             enforceUid(Binder.getCallingUid());
             if (secret == null || secret.length != FRP_SECRET_SIZE) {
                 throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index a64d1ae..05e8f9a 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -122,6 +122,7 @@
 public class PackageArchiver {
 
     private static final String TAG = "PackageArchiverService";
+    private static final boolean DEBUG = true;
 
     public static final String EXTRA_UNARCHIVE_INTENT_SENDER =
             "android.content.pm.extra.UNARCHIVE_INTENT_SENDER";
@@ -362,6 +363,10 @@
                     // TODO(b/319238030) Move this into installd.
                     if (!FileUtils.deleteContentsAndDir(iconsDir)) {
                         Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+                    } else {
+                        if (DEBUG) {
+                            Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath());
+                        }
                     }
                 });
     }
@@ -524,6 +529,9 @@
             }
             out.flush();
         }
+        if (DEBUG && iconFile.exists()) {
+            Slog.i(TAG, "Stored icon at " + iconFile.getAbsolutePath());
+        }
         return iconFile.toPath();
     }
 
@@ -1194,6 +1202,9 @@
             if (!iconsDir.isDirectory()) {
                 throw new IOException("Unable to create directory " + iconsDir);
             }
+            if (DEBUG) {
+                Slog.i(TAG, "Created icons directory at " + iconsDir.getAbsolutePath());
+            }
         }
         SELinux.restorecon(iconsDir);
         return iconsDir;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 29242aa..fe65010 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5358,9 +5358,16 @@
             pw.println(sdf.format(date));
 
             if (pus.getArchiveState() != null) {
+                final ArchiveState archiveState = pus.getArchiveState();
                 pw.print("      archiveTime=");
-                date.setTime(pus.getArchiveState().getArchiveTimeMillis());
+                date.setTime(archiveState.getArchiveTimeMillis());
                 pw.println(sdf.format(date));
+                pw.print("      unarchiveInstallerTitle=");
+                pw.println(archiveState.getInstallerTitle());
+                for (ArchiveState.ArchiveActivityInfo activity : archiveState.getActivityInfos()) {
+                    pw.print("        archiveActivityInfo=");
+                    pw.println(activity.toString());
+                }
             }
 
             pw.print("      uninstallReason=");
@@ -5475,10 +5482,6 @@
                     }
                 }
             }
-            ArchiveState archiveState = userState.getArchiveState();
-            if (archiveState != null) {
-                pw.print(archiveState.toString());
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 8637d91..b3c8b0b 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -85,6 +85,7 @@
 
     private final SystemInterface mSystemInterface;
     private final Context mContext;
+    private final WebViewProviderInfo mDefaultProvider;
 
     private long mMinimumVersionCode = -1;
 
@@ -105,6 +106,16 @@
     WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) {
         mContext = context;
         mSystemInterface = systemInterface;
+        WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+        for (WebViewProviderInfo provider : webviewProviders) {
+            if (provider.availableByDefault) {
+                mDefaultProvider = provider;
+                break;
+            }
+        }
+        // This should be unreachable because the config parser enforces that there is at least one
+        // availableByDefault provider.
+        throw new AndroidRuntimeException("No available by default WebView Provider.");
     }
 
     @Override
@@ -163,11 +174,10 @@
         if (mCurrentWebViewPackage == null) {
             return true;
         }
-        WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
-        if (mCurrentWebViewPackage.packageName.equals(defaultProvider.packageName)) {
+        if (mCurrentWebViewPackage.packageName.equals(mDefaultProvider.packageName)) {
             List<UserPackage> userPackages =
                     mSystemInterface.getPackageInfoForProviderAllUsers(
-                            mContext, defaultProvider);
+                            mContext, mDefaultProvider);
             return !isInstalledAndEnabledForAllUsers(userPackages);
         } else {
             return false;
@@ -200,13 +210,12 @@
                 // default package for all users in case it was disabled, even if we already did the
                 // one-time migration before. If this actually changes the state, we will see the
                 // PackageManager broadcast shortly and try again.
-                WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
                 Slog.w(
                         TAG,
                         "No provider available for all users, trying to enable "
-                                + defaultProvider.packageName);
+                                + mDefaultProvider.packageName);
                 mSystemInterface.enablePackageForAllUsers(
-                        mContext, defaultProvider.packageName, true);
+                        mContext, mDefaultProvider.packageName, true);
             }
 
         } catch (Throwable t) {
@@ -398,15 +407,7 @@
      */
     @Override
     public WebViewProviderInfo getDefaultWebViewPackage() {
-        WebViewProviderInfo[] webviewProviders = getWebViewPackages();
-        for (WebViewProviderInfo provider : webviewProviders) {
-            if (provider.availableByDefault) {
-                return provider;
-            }
-        }
-        // This should be unreachable because the config parser enforces that there is at least one
-        // availableByDefault provider.
-        throw new AndroidRuntimeException("No available by default WebView Provider.");
+        return mDefaultProvider;
     }
 
     private static class ProviderAndPackageInfo {
@@ -467,14 +468,13 @@
 
         // User did not choose, or the choice failed; return the default provider even if it is not
         // installed or enabled for all users.
-        WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
         try {
-            PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(defaultProvider);
-            if (validityResult(defaultProvider, packageInfo) == VALIDITY_OK) {
+            PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(mDefaultProvider);
+            if (validityResult(mDefaultProvider, packageInfo) == VALIDITY_OK) {
                 return packageInfo;
             }
         } catch (NameNotFoundException e) {
-            Slog.w(TAG, "Default WebView package (" + defaultProvider.packageName + ") not found");
+            Slog.w(TAG, "Default WebView package (" + mDefaultProvider.packageName + ") not found");
         }
 
         // This should never happen during normal operation (only with modified system images).
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
index da8ec2e..f91f77a 100644
--- a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -17,12 +17,17 @@
 package com.android.server.pdb;
 
 import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_CREDENTIAL_RESERVED_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_MAGIC;
 import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.HEADER_SIZE;
 import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE;
 import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE;
 import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.TEST_MODE_RESERVED_SIZE;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,6 +41,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -63,6 +69,7 @@
 import java.nio.file.StandardOpenOption;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
 
 @RunWith(JUnitParamsRunner.class)
 public class PersistentDataBlockServiceTest {
@@ -397,6 +404,68 @@
 
     @Test
     @Parameters({"false", "true"})
+    public void testPartitionFormat(boolean frpEnabled) throws Exception {
+        setUp(frpEnabled);
+
+        /*
+         * 1. Fill the PDB with a specific value, so we can check regions that weren't touched
+         *    by formatting
+         */
+        FileChannel channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE,
+                StandardOpenOption.TRUNCATE_EXISTING);
+        byte[] bufArray = new byte[(int) mPdbService.getBlockDeviceSize()];
+        Arrays.fill(bufArray, (byte) 0x7f);
+        ByteBuffer buf = ByteBuffer.wrap(bufArray);
+        channel.write(buf);
+        channel.close();
+
+        /*
+         * 2. Format it.
+         */
+        mPdbService.formatPartitionLocked(true);
+
+        /*
+         * 3. Check it.
+         */
+        channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ);
+
+        // 3a. Skip the digest and header
+        channel.position(channel.position() + DIGEST_SIZE_BYTES + HEADER_SIZE);
+
+        // 3b. Check the FRP data segment
+        assertContains("FRP data", readData(channel, mPdbService.getMaximumFrpDataSize()).array(),
+                (byte) 0);
+
+        if (frpEnabled) {
+            // 3c. The FRP secret magic & value
+            assertThat(mPdbService.getFrpSecretMagicOffset()).isEqualTo(channel.position());
+            assertThat(readData(channel, FRP_SECRET_MAGIC.length).array()).isEqualTo(
+                    FRP_SECRET_MAGIC);
+
+            assertThat(mPdbService.getFrpSecretDataOffset()).isEqualTo(channel.position());
+            assertContains("FRP secret", readData(channel, FRP_SECRET_SIZE).array(), (byte) 0);
+        }
+
+        // 3d. The test mode data (unmodified by formatPartitionLocked()).
+        assertThat(mPdbService.getTestHarnessModeDataOffset()).isEqualTo(channel.position());
+        assertContains("Test data", readData(channel, TEST_MODE_RESERVED_SIZE).array(),
+                (byte) 0x7f);
+
+        // 3e. The FRP credential segment
+        assertThat(mPdbService.getFrpCredentialDataOffset()).isEqualTo(channel.position());
+        assertContains("FRP credential", readData(channel, FRP_CREDENTIAL_RESERVED_SIZE).array(),
+                (byte) 0);
+
+        // 3f. OEM unlock byte.
+        assertThat(mPdbService.getOemUnlockDataOffset()).isEqualTo(channel.position());
+        assertThat(new byte[]{1}).isEqualTo(readData(channel, 1).array());
+
+        // 3g. EOF
+        assertThat(channel.position()).isEqualTo(channel.size());
+    }
+
+    @Test
+    @Parameters({"false", "true"})
     public void wipePermissionCheck(boolean frpEnabled) throws Exception {
         setUp(frpEnabled);
         denyOemUnlockPermission();
@@ -987,4 +1056,20 @@
             return buffer;
         }
     }
+
+    @NonNull
+    private static ByteBuffer readData(FileChannel channel, int length) throws IOException {
+        ByteBuffer buf = ByteBuffer.allocate(length);
+        assertThat(channel.read(buf)).isEqualTo(length);
+        buf.flip();
+        assertThat(buf.limit()).isEqualTo(length);
+        return buf;
+    }
+
+    private static void assertContains(String sectionName, byte[] buf, byte expected) {
+        for (int i = 0; i < buf.length; i++) {
+            assertWithMessage(sectionName + " is incorrect at offset " + i)
+                    .that(buf[i]).isEqualTo(expected);
+        }
+    }
 }