Adding ColorUpdateLogger to track color updates so that we demonstrate the root cause of our theme bugs

Bug: 294347738
Test: dumpsysui ColorUpdateLogger
Change-Id: I4879dd28a8d88b3ee6fc1f09c9029eb653bfa460
Flag: ACONFIG com.android.systemui.notification_color_update_logger DEVELOPMENT
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2c285c8..2240843 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 "
@@ -452,4 +459,3 @@
         purpose: PURPOSE_BUGFIX
     }
 }
-
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/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/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/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";
+    }
 }