diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9801a0394ffa779f51d02b3484f89b0aec782150..33361721dea40f9bc504c5d535f0b4db0285c51f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10664,6 +10664,13 @@ public final class Settings {
          */
         public static final String LOCKSCREEN_MEDIA_METADATA = "lockscreen_media_metadata";
 
+        /**
+         * Show music visualizer on the lockscreen when playing.
+         *
+         * @hide
+         */
+        public static final String LOCKSCREEN_VISUALIZER_ENABLED = "lockscreen_visualizer";
+
         /**
          * Check the proximity sensor during wakeup
          *
diff --git a/packages/SystemUI/LMODroidManifest.xml b/packages/SystemUI/LMODroidManifest.xml
index f39a959c2b33e9cc27baad6625e0fc40eb65a8cc..127887ad77e302d0151c272b190de14f02632763 100644
--- a/packages/SystemUI/LMODroidManifest.xml
+++ b/packages/SystemUI/LMODroidManifest.xml
@@ -23,6 +23,10 @@
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
 
+    <!-- Visualizer -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
     <!-- SystemUI Tuner -->
     <application>
         <activity-alias
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index b28cb2f6f483cc01a0e40630ab7d21b09cc594dd..9f810e2433c1b5c13bd1a969d54b087a83f58625 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -59,6 +59,16 @@
         sysui:ignoreRightInset="true"
         />
 
+    <com.android.systemui.statusbar.VisualizerView
+        android:id="@+id/visualizerview"
+        android:gravity="bottom"
+        android:layout_gravity="bottom"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="visible"
+        sysui:ignoreRightInset="true"
+    />
+
     <com.android.systemui.statusbar.LightRevealScrim
             android:id="@+id/light_reveal_scrim"
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d3ccc05b90a6732baf2c8ca8bdb9e3cd90fdc864..22d08cc3e82ab4d276388497c57a1d9342b2e4c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -24,6 +24,7 @@ import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -138,6 +139,7 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
     private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final MediaArtworkProcessor mMediaArtworkProcessor;
     private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
+    private final WallpaperManager mWallpaperManager;
 
     protected NotificationPresenter mPresenter;
     private MediaController mMediaController;
@@ -162,6 +164,8 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
                     clearCurrentMediaNotification();
                 }
                 findAndUpdateMediaNotifications();
+                mStatusBarOptionalLazy.get().map(StatusBar::getVisualizerView).ifPresent(
+                        v -> v.setOccluded(state.getState() == PlaybackState.STATE_PLAYING));
             }
         }
 
@@ -192,7 +196,8 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
             FeatureFlags featureFlags,
             @Main DelayableExecutor mainExecutor,
             MediaDataManager mediaDataManager,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            WallpaperManager wallpaperManager) {
         mContext = context;
         mMediaArtworkProcessor = mediaArtworkProcessor;
         mKeyguardBypassController = keyguardBypassController;
@@ -215,6 +220,8 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
         }
 
 
+        mWallpaperManager = wallpaperManager;
+
         final TunerService tunerService = Dependency.get(TunerService.class);
         tunerService.addTunable(this, LOCKSCREEN_MEDIA_METADATA);
     }
@@ -667,9 +674,10 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
         }
         boolean hasMediaArtwork = artworkDrawable != null;
         boolean allowWhenShade = false;
+        Bitmap lockWallpaper = null;
         // if no media artwork, show normal lockscreen wallpaper
         if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
-            Bitmap lockWallpaper =
+            lockWallpaper =
                     mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
             if (lockWallpaper != null) {
                 artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
@@ -691,6 +699,34 @@ public class NotificationMediaManager implements Dumpable, TunerService.Tunable
             mScrimController.setHasBackdrop(hasArtwork);
         }
 
+        if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
+            boolean isScreenFullyOff =
+                    !mStatusBarOptionalLazy.get().map(StatusBar::isScreenFullyOff).orElse(false);
+            if (!mKeyguardStateController.isKeyguardFadingAway() && isScreenFullyOff) {
+                mStatusBarOptionalLazy.get().map(StatusBar::getVisualizerView).ifPresent(
+                        v -> v.setPlaying(getMediaControllerPlaybackState(mMediaController)
+                                == PlaybackState.STATE_PLAYING));
+            }
+
+            Bitmap bitmap;
+
+            if (artworkDrawable instanceof BitmapDrawable) {
+                // always use current backdrop to color eq
+                bitmap = ((BitmapDrawable) artworkDrawable).getBitmap();
+            } else if (lockWallpaper instanceof Bitmap) {
+                // use lockscreen wallpaper in case user set one
+                bitmap = lockWallpaper.getConfig() == Bitmap.Config.HARDWARE
+                        ? lockWallpaper.copy(Bitmap.Config.ARGB_8888, false)
+                        : lockWallpaper;
+            } else {
+                // use regular wallpaper
+                bitmap = mWallpaperManager.getBitmap(false);
+            }
+
+            mStatusBarOptionalLazy.get().map(StatusBar::getVisualizerView).ifPresent(
+                    v -> v.setBitmap(bitmap));
+        }
+
         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
                 && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
                 &&  mBiometricUnlockController != null && mBiometricUnlockController.getMode()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff3861b39aba0385b985f3a367ed78d782504557
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VisualizerView.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *               2017-2019 The LineageOS 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;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.media.audiofx.Visualizer;
+import android.os.AsyncTask;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.graphics.palette.Palette;
+import com.android.internal.graphics.palette.Palette.Swatch;
+import com.android.systemui.Dependency;
+import com.android.systemui.tuner.TunerService;
+
+public class VisualizerView extends View
+        implements Palette.PaletteAsyncListener, TunerService.Tunable {
+
+    private static final String TAG = VisualizerView.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private static final String LOCKSCREEN_VISUALIZER_ENABLED =
+            "customsecure:" + Settings.Secure.LOCKSCREEN_VISUALIZER_ENABLED;
+
+    private Paint mPaint;
+    private Visualizer mVisualizer;
+    private ObjectAnimator mVisualizerColorAnimator;
+
+    private ValueAnimator[] mValueAnimators;
+    private float[] mFFTPoints;
+
+    private int mStatusBarState;
+    private boolean mVisualizerEnabled;
+    private boolean mVisible;
+    private boolean mPlaying;
+    private boolean mDisplaying; // the state we're animating to
+    private boolean mDozing;
+    private boolean mOccluded;
+
+    private int mColor;
+    private Bitmap mCurrentBitmap;
+
+    private Visualizer.OnDataCaptureListener mVisualizerListener =
+            new Visualizer.OnDataCaptureListener() {
+        byte rfk, ifk;
+        int dbValue;
+        float magnitude;
+
+        @Override
+        public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
+        }
+
+        @Override
+        public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
+            for (int i = 0; i < 32; i++) {
+                mValueAnimators[i].cancel();
+                rfk = fft[i * 2 + 2];
+                ifk = fft[i * 2 + 3];
+                magnitude = rfk * rfk + ifk * ifk;
+                dbValue = magnitude > 0 ? (int) (10 * Math.log10(magnitude)) : 0;
+
+                mValueAnimators[i].setFloatValues(mFFTPoints[i * 4 + 1],
+                        mFFTPoints[3] - (dbValue * 16f));
+                mValueAnimators[i].start();
+            }
+        }
+    };
+
+    private final Runnable mLinkVisualizer = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) {
+                Log.w(TAG, "+++ mLinkVisualizer run()");
+            }
+
+            try {
+                mVisualizer = new Visualizer(0);
+            } catch (Exception e) {
+                Log.e(TAG, "error initializing visualizer", e);
+                return;
+            }
+
+            mVisualizer.setEnabled(false);
+            mVisualizer.setCaptureSize(66);
+            mVisualizer.setDataCaptureListener(mVisualizerListener, Visualizer.getMaxCaptureRate(),
+                    false, true);
+            mVisualizer.setEnabled(true);
+
+            if (DEBUG) {
+                Log.w(TAG, "--- mLinkVisualizer run()");
+            }
+        }
+    };
+
+    private final Runnable mAsyncUnlinkVisualizer = new Runnable() {
+        @Override
+        public void run() {
+            AsyncTask.execute(mUnlinkVisualizer);
+        }
+    };
+
+    private final Runnable mUnlinkVisualizer = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG) {
+                Log.w(TAG, "+++ mUnlinkVisualizer run(), mVisualizer: " + mVisualizer);
+            }
+            if (mVisualizer != null) {
+                mVisualizer.setEnabled(false);
+                mVisualizer.release();
+                mVisualizer = null;
+            }
+            if (DEBUG) {
+                Log.w(TAG, "--- mUnlinkVisualizer run()");
+            }
+        }
+    };
+
+    public VisualizerView(Context context) {
+        this(context, null, 0);
+    }
+
+    public VisualizerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public VisualizerView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mColor = Color.TRANSPARENT;
+
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(mColor);
+
+        mFFTPoints = new float[128];
+        mValueAnimators = new ValueAnimator[32];
+        for (int i = 0; i < 32; i++) {
+            final int j = i * 4 + 1;
+            mValueAnimators[i] = new ValueAnimator();
+            mValueAnimators[i].setDuration(128);
+            mValueAnimators[i].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mFFTPoints[j] = (float) animation.getAnimatedValue();
+                    postInvalidate();
+                }
+            });
+        }
+    }
+
+    private void updateViewVisibility() {
+        final int curVis = getVisibility();
+        final int newVis = mStatusBarState != StatusBarState.SHADE &&
+                mVisualizerEnabled ? View.VISIBLE : View.GONE;
+        if (curVis != newVis) {
+            setVisibility(newVis);
+            checkStateChanged();
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        Dependency.get(TunerService.class).addTunable(this, LOCKSCREEN_VISUALIZER_ENABLED);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        Dependency.get(TunerService.class).removeTunable(this);
+        mCurrentBitmap = null;
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (LOCKSCREEN_VISUALIZER_ENABLED.equals(key)) {
+            mVisualizerEnabled = TunerService.parseIntegerSwitch(newValue, false);
+            checkStateChanged();
+            updateViewVisibility();
+        }
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        float barUnit = w / 32f;
+        float barWidth = barUnit * 8f / 9f;
+        barUnit = barWidth + (barUnit - barWidth) * 32f / 31f;
+        mPaint.setStrokeWidth(barWidth);
+
+        for (int i = 0; i < 32; i++) {
+            mFFTPoints[i * 4] = mFFTPoints[i * 4 + 2] = i * barUnit + (barWidth / 2);
+            mFFTPoints[i * 4 + 1] = h;
+            mFFTPoints[i * 4 + 3] = h;
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mVisualizer != null) {
+            canvas.drawLines(mFFTPoints, mPaint);
+        }
+    }
+
+    @Override
+    public void onGenerated(Palette palette) {
+        Swatch dominantSwatch = palette.getDominantSwatch();
+        if (dominantSwatch != null) {
+            setColor(dominantSwatch.getInt());
+        }
+    }
+
+    public void setDozing(boolean dozing) {
+        if (mDozing != dozing) {
+            if (DEBUG) {
+                Log.i(TAG, "setDozing() called with dozing = [" + dozing + "]");
+            }
+            mDozing = dozing;
+            checkStateChanged();
+        }
+    }
+
+    public void setOccluded(boolean occluded) {
+        if (mOccluded != occluded) {
+            if (DEBUG) {
+                Log.i(TAG, "setOccluded() called with occluded = [" + occluded + "]");
+            }
+            mOccluded = occluded;
+            checkStateChanged();
+        }
+    }
+
+    public void setPlaying(boolean playing) {
+        if (mPlaying != playing) {
+            if (DEBUG) {
+                Log.i(TAG, "setPlaying() called with playing = [" + playing + "]");
+            }
+            mPlaying = playing;
+            checkStateChanged();
+        }
+    }
+
+    public void setVisible(boolean visible) {
+        if (mVisible != visible) {
+            if (DEBUG) {
+                Log.i(TAG, "setVisible() called with visible = [" + visible + "]");
+            }
+            mVisible = visible;
+            checkStateChanged();
+        }
+    }
+
+    public void setStatusBarState(int statusBarState) {
+        if (mStatusBarState != statusBarState) {
+            mStatusBarState = statusBarState;
+            updateViewVisibility();
+        }
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        if (mCurrentBitmap == bitmap) {
+            return;
+        }
+        mCurrentBitmap = bitmap;
+        if (bitmap != null) {
+            Palette.from(bitmap, null).generate(this);
+        } else {
+            setColor(Color.TRANSPARENT);
+        }
+    }
+
+    private void setColor(int color) {
+        if (color == Color.TRANSPARENT) {
+            color = Color.WHITE;
+        }
+
+        color = Color.argb(140, Color.red(color), Color.green(color), Color.blue(color));
+
+        if (mColor != color) {
+            mColor = color;
+
+            if (mVisualizer != null) {
+                if (mVisualizerColorAnimator != null) {
+                    mVisualizerColorAnimator.cancel();
+                }
+
+                mVisualizerColorAnimator = ObjectAnimator.ofArgb(mPaint, "color",
+                        mPaint.getColor(), mColor);
+                mVisualizerColorAnimator.setStartDelay(600);
+                mVisualizerColorAnimator.setDuration(1200);
+                mVisualizerColorAnimator.start();
+            } else {
+                mPaint.setColor(mColor);
+            }
+        }
+    }
+
+    private void checkStateChanged() {
+        if (getVisibility() == View.VISIBLE && mVisible && mVisualizerEnabled && mPlaying &&
+                !mDozing && !mOccluded) {
+            if (!mDisplaying) {
+                mDisplaying = true;
+                AsyncTask.execute(mLinkVisualizer);
+                animate()
+                        .alpha(1f)
+                        .withEndAction(null)
+                        .setDuration(800);
+            }
+        } else {
+            if (mDisplaying) {
+                mDisplaying = false;
+                animate()
+                        .alpha(0f)
+                        .withEndAction(mAsyncUnlinkVisualizer)
+                        .setDuration(mVisible ? 600 : 0);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index f2d926d97108ad6c19d252163ae07a3ac1b22e2a..cfdfa86acc3c81fd2dc31aeeaf248e5319a39669 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.dagger;
 
 import android.app.IActivityManager;
 import android.app.NotificationManager;
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.os.Handler;
 import android.service.dreams.IDreamManager;
@@ -139,7 +140,8 @@ public interface StatusBarDependenciesModule {
             FeatureFlags featureFlags,
             @Main DelayableExecutor mainExecutor,
             MediaDataManager mediaDataManager,
-            DumpManager dumpManager) {
+            DumpManager dumpManager,
+            WallpaperManager wallpaperManager) {
         return new NotificationMediaManager(
                 context,
                 statusBarOptionalLazy,
@@ -152,7 +154,8 @@ public interface StatusBarDependenciesModule {
                 featureFlags,
                 mainExecutor,
                 mediaDataManager,
-                dumpManager);
+                dumpManager,
+                wallpaperManager);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 46e6fb45d3219544276e860ba96025df9cb11f48..076d7e8b8149f0fb38b2196549ab8ce836091bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -201,6 +201,7 @@ import com.android.systemui.statusbar.PowerButtonReveal;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VisualizerView;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -480,6 +481,7 @@ public class StatusBar extends SystemUI implements
     protected PhoneStatusBarView mStatusBarView;
     private PhoneStatusBarViewController mPhoneStatusBarViewController;
     private AuthRippleController mAuthRippleController;
+    private VisualizerView mVisualizerView;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     protected NotificationShadeWindowController mNotificationShadeWindowController;
     private final StatusBarWindowController mStatusBarWindowController;
@@ -1585,6 +1587,7 @@ public class StatusBar extends SystemUI implements
         mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
 
         mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView();
+        mVisualizerView = mStatusBarComponent.getVisualizerView();
         mNotificationShadeWindowViewController = mStatusBarComponent
                 .getNotificationShadeWindowViewController();
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
@@ -1683,6 +1686,10 @@ public class StatusBar extends SystemUI implements
         return mNotificationShadeWindowViewController.getBouncerContainer();
     }
 
+    public VisualizerView getVisualizerView() {
+        return mVisualizerView;
+    }
+
     public int getStatusBarHeight() {
         return mStatusBarWindowController.getStatusBarHeight();
     }
@@ -3370,6 +3377,7 @@ public class StatusBar extends SystemUI implements
                 && visibleNotOccludedOrWillBe);
 
         mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
+        mVisualizerView.setDozing(mDozing);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3797,12 +3805,14 @@ public class StatusBar extends SystemUI implements
         @Override
         public void onScreenTurnedOn() {
             mScrimController.onScreenTurnedOn();
+            mVisualizerView.setVisible(true);
         }
 
         @Override
         public void onScreenTurnedOff() {
             mFalsingCollector.onScreenOff();
             mScrimController.onScreenTurnedOff();
+            mVisualizerView.setVisible(false);
             updateIsKeyguard();
         }
     };
@@ -4542,6 +4552,7 @@ public class StatusBar extends SystemUI implements
                     checkBarModes();
                     updateScrimController();
                     mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
+                    mVisualizerView.setStatusBarState(newState);
                     updateKeyguardState();
                     Trace.endSection();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e7889286d1953d67ec9944a1d52fcd12538d40ad..b562ed25aa6ab7f50c29b47123b34d5b5cff3cd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -72,6 +72,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -116,6 +117,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
     private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
     private final Lazy<ShadeController> mShadeController;
+    private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -247,7 +249,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
             WakefulnessLifecycle wakefulnessLifecycle,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
-            Lazy<ShadeController> shadeController) {
+            Lazy<ShadeController> shadeController,
+            Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -264,6 +267,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
         mShadeController = shadeController;
+        mStatusBarOptionalLazy = statusBarOptionalLazy;
     }
 
     @Override
@@ -676,6 +680,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
             mMediaManager.updateMediaMetaData(false, animate && !occluded);
         }
         mNotificationShadeWindowController.setKeyguardOccluded(occluded);
+        mStatusBarOptionalLazy.get().map(StatusBar::getVisualizerView).ifPresent(
+                v -> v.setOccluded(occluded));
 
         // setDozing(false) will call reset once we stop dozing.
         if (!mDozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index 375641fdb69dea8979849bc9aaf29e2f0ba1d46d..103987d061ccacfab01e5c990acfa0bc05abd295 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
 import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.VisualizerView;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -73,6 +74,10 @@ public interface StatusBarComponent {
     @StatusBarScope
     NotificationShadeWindowView getNotificationShadeWindowView();
 
+    /** */
+    @StatusBarScope
+    VisualizerView getVisualizerView();
+
     /** */
     @StatusBarScope
     NotificationShelfController getNotificationShelfController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 14cca13b396b3175292372c24417e6b44bc5ba2b..27d001dba5691ac5cd79b514245800fd6132bea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManage
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.VisualizerView;
 import com.android.systemui.tuner.TunerService;
 
 import javax.inject.Named;
@@ -88,6 +89,14 @@ public abstract class StatusBarViewModule {
         return notificationShadeWindowView;
     }
 
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    public static VisualizerView providesVisualizerView(
+            NotificationShadeWindowView notificationShadeWindowView) {
+        return notificationShadeWindowView.findViewById(R.id.visualizerview);
+    }
+
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope