Merge "Refactor - Pull status bar "orchestration" logic out of CentralSurfaces" into main
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 55ccaa6..92bc95a 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -70,4 +70,12 @@
      * {@link #onBootCompleted()} will never be called before {@link #start()}. */
     default void onBootCompleted() {
     }
+
+    /** No op implementation that can be used when feature flagging on the Dagger Module level. */
+    CoreStartable NOP = new Nop();
+
+    class Nop implements CoreStartable {
+        @Override
+        public void start() {}
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
new file mode 100644
index 0000000..57c8bc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.os.Binder
+import android.os.RemoteException
+import android.view.WindowInsets
+import com.android.internal.statusbar.IStatusBarService
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.systemui.CoreStartable
+import com.android.systemui.InitController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import dagger.Lazy
+import javax.inject.Inject
+
+@SysUISingleton
+class CommandQueueInitializer
+@Inject
+constructor(
+    private val context: Context,
+    private val commandQueue: CommandQueue,
+    private val commandQueueCallbacksLazy: Lazy<CommandQueue.Callbacks>,
+    private val statusBarModeRepository: StatusBarModeRepositoryStore,
+    private val initController: InitController,
+    private val barService: IStatusBarService,
+    private val navigationBarController: NavigationBarController,
+) : CoreStartable {
+
+    override fun start() {
+        StatusBarSimpleFragment.assertInNewMode()
+        val result: RegisterStatusBarResult =
+            try {
+                barService.registerStatusBar(commandQueue)
+            } catch (ex: RemoteException) {
+                ex.rethrowFromSystemServer()
+                return
+            }
+
+        createNavigationBar(result)
+
+        if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
+            statusBarModeRepository.defaultDisplay.showTransient()
+        }
+        val displayId = context.display.displayId
+        val commandQueueCallbacks = commandQueueCallbacksLazy.get()
+        commandQueueCallbacks.onSystemBarAttributesChanged(
+            displayId,
+            result.mAppearance,
+            result.mAppearanceRegions,
+            result.mNavbarColorManagedByIme,
+            result.mBehavior,
+            result.mRequestedVisibleTypes,
+            result.mPackageName,
+            result.mLetterboxDetails,
+        )
+
+        // StatusBarManagerService has a back up of IME token and it's restored here.
+        commandQueueCallbacks.setImeWindowStatus(
+            displayId,
+            result.mImeWindowVis,
+            result.mImeBackDisposition,
+            result.mShowImeSwitcher,
+        )
+
+        // Set up the initial icon state
+        val numIcons: Int = result.mIcons.size
+        for (i in 0 until numIcons) {
+            commandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i))
+        }
+
+        // set the initial view visibility
+        val disabledFlags1 = result.mDisabledFlags1
+        val disabledFlags2 = result.mDisabledFlags2
+        initController.addPostInitTask {
+            commandQueue.disable(displayId, disabledFlags1, disabledFlags2, /* animate= */ false)
+            try {
+                // NOTE(b/262059863): Force-update the disable flags after applying the flags
+                // returned from registerStatusBar(). The result's disabled flags may be stale
+                // if StatusBarManager's disabled flags are updated between registering the bar
+                // and this handling this post-init task. We force an update in this case, and use a
+                // new token to not conflict with any other disabled flags already requested by
+                // SysUI
+                val token = Binder()
+                barService.disable(StatusBarManager.DISABLE_HOME, token, context.packageName)
+                barService.disable(0, token, context.packageName)
+            } catch (ex: RemoteException) {
+                ex.rethrowFromSystemServer()
+            }
+        }
+    }
+
+    private fun createNavigationBar(result: RegisterStatusBarResult) {
+        navigationBarController.createNavigationBars(/* includeDefaultDisplay= */ true, result)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
new file mode 100644
index 0000000..8bd990b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import android.view.View
+import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.PluginDependencyProvider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.shade.NotificationShadeWindowViewController
+import com.android.systemui.shade.ShadeSurface
+import com.android.systemui.statusbar.AutoHideUiElement
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.phone.AutoHideController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.wm.shell.bubbles.Bubbles
+import dagger.Lazy
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.launch
+
+/**
+ * Class responsible for managing the lifecycle and state of the status bar.
+ *
+ * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
+ * plan is break it out into individual classes.
+ */
+@SysUISingleton
+class StatusBarOrchestrator
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val statusBarInitializer: StatusBarInitializer,
+    private val statusBarWindowController: StatusBarWindowController,
+    private val statusBarModeRepository: StatusBarModeRepositoryStore,
+    private val demoModeController: DemoModeController,
+    private val pluginDependencyProvider: PluginDependencyProvider,
+    private val autoHideController: AutoHideController,
+    private val remoteInputManager: NotificationRemoteInputManager,
+    private val notificationShadeWindowViewControllerLazy:
+    Lazy<NotificationShadeWindowViewController>,
+    private val shadeSurface: ShadeSurface,
+    private val bubblesOptional: Optional<Bubbles>,
+    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+    powerInteractor: PowerInteractor,
+    primaryBouncerInteractor: PrimaryBouncerInteractor,
+) : CoreStartable {
+
+    private val phoneStatusBarViewController =
+        MutableStateFlow<PhoneStatusBarViewController?>(value = null)
+
+    private val phoneStatusBarTransitions =
+        MutableStateFlow<PhoneStatusBarTransitions?>(value = null)
+
+    private val shouldAnimateNextBarModeChange =
+        combine(
+            statusBarModeRepository.defaultDisplay.isTransientShown,
+            powerInteractor.isAwake,
+            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+        ) { isTransientShown, isDeviceAwake, statusBarWindowState ->
+            !isTransientShown &&
+                isDeviceAwake &&
+                statusBarWindowState != StatusBarWindowState.Hidden
+        }
+
+    private val controllerAndBouncerShowing =
+        combine(
+            phoneStatusBarViewController.filterNotNull(),
+            primaryBouncerInteractor.isShowing,
+            ::Pair,
+        )
+
+    private val barTransitionsAndDeviceAsleep =
+        combine(phoneStatusBarTransitions.filterNotNull(), powerInteractor.isAsleep, ::Pair)
+
+    private val statusBarVisible =
+        combine(
+            statusBarModeRepository.defaultDisplay.statusBarMode,
+            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+        ) { mode, statusBarWindowState ->
+            mode != StatusBarMode.LIGHTS_OUT &&
+                mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
+                statusBarWindowState != StatusBarWindowState.Hidden
+        }
+
+    private val barModeUpdate =
+        combine(
+                shouldAnimateNextBarModeChange,
+                phoneStatusBarTransitions.filterNotNull(),
+                statusBarModeRepository.defaultDisplay.statusBarMode,
+                ::Triple,
+            )
+            .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
+                // We only want to collect when either bar transitions or status bar mode
+                // changed.
+                Pair(barTransitions, statusBarMode)
+            }
+
+    override fun start() {
+        StatusBarSimpleFragment.assertInNewMode()
+        applicationScope.launch {
+            launch {
+                controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+                    setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+                }
+            }
+            launch {
+                barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+                    if (deviceAsleep) {
+                        barTransitions.finishAnimations()
+                    }
+                }
+            }
+            launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+            launch {
+                barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+                    updateBarMode(animate, barTransitions, statusBarMode)
+                }
+            }
+        }
+        createAndAddWindow()
+        setupPluginDependencies()
+        setUpAutoHide()
+    }
+
+    private fun createAndAddWindow() {
+        initializeStatusBarFragment()
+        statusBarWindowController.attach()
+    }
+
+    private fun initializeStatusBarFragment() {
+        statusBarInitializer.statusBarViewUpdatedListener =
+            object : StatusBarInitializer.OnStatusBarViewUpdatedListener {
+                override fun onStatusBarViewUpdated(
+                    statusBarViewController: PhoneStatusBarViewController,
+                    statusBarTransitions: PhoneStatusBarTransitions,
+                ) {
+                    phoneStatusBarViewController.value = statusBarViewController
+                    phoneStatusBarTransitions.value = statusBarTransitions
+
+                    notificationShadeWindowViewControllerLazy
+                        .get()
+                        .setStatusBarViewController(statusBarViewController)
+                    // Ensure we re-propagate panel expansion values to the panel controller and
+                    // any listeners it may have, such as PanelBar. This will also ensure we
+                    // re-display the notification panel if necessary (for example, if
+                    // a heads-up notification was being displayed and should continue being
+                    // displayed).
+                    shadeSurface.updateExpansionAndVisibility()
+                }
+            }
+    }
+
+    private fun setupPluginDependencies() {
+        pluginDependencyProvider.allowPluginDependency(DarkIconDispatcher::class.java)
+        pluginDependencyProvider.allowPluginDependency(StatusBarStateController::class.java)
+    }
+
+    private fun setUpAutoHide() {
+        autoHideController.setStatusBar(
+            object : AutoHideUiElement {
+                override fun synchronizeState() {}
+
+                override fun shouldHideOnTouch(): Boolean {
+                    return !remoteInputManager.isRemoteInputActive
+                }
+
+                override fun isVisible(): Boolean {
+                    return statusBarModeRepository.defaultDisplay.isTransientShown.value
+                }
+
+                override fun hide() {
+                    statusBarModeRepository.defaultDisplay.clearTransient()
+                }
+            })
+    }
+
+    private fun updateBarMode(
+        animate: Boolean,
+        barTransitions: PhoneStatusBarTransitions,
+        barMode: StatusBarMode,
+    ) {
+        if (!demoModeController.isInDemoMode) {
+            barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
+        }
+        autoHideController.touchAutoHide()
+    }
+
+    private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+        bubblesOptional.ifPresent { bubbles: Bubbles ->
+            bubbles.onStatusBarVisibilityChanged(statusBarVisible)
+        }
+    }
+
+    private fun setBouncerShowingForStatusBarComponents(
+        controller: PhoneStatusBarViewController,
+        bouncerShowing: Boolean,
+    ) {
+        val importance =
+            if (bouncerShowing) {
+                View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+            } else {
+                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+            }
+        controller.setImportantForAccessibility(importance)
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+        CentralSurfaces.dumpBarTransitions(
+            pw,
+            "PhoneStatusBarTransitions",
+            phoneStatusBarTransitions.value,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 3903ff3..cf238d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -46,6 +46,7 @@
  */
 @Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
 abstract class StatusBarModule {
+
     @Binds
     @IntoMap
     @ClassKey(OngoingCallController::class)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 1d3f0e1..5f4f72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -241,10 +241,10 @@
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
-import dagger.Lazy;
-
 import dalvik.annotation.optimization.NeverCompile;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Map;
@@ -304,6 +304,7 @@
             };
 
     void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         mStatusBarWindowState = state;
         updateBubblesVisibility();
     }
@@ -813,8 +814,9 @@
         mStartingSurfaceOptional = startingSurfaceOptional;
         mDreamManager = dreamManager;
         lockscreenShadeTransitionController.setCentralSurfaces(this);
-        statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
-
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
+        }
         mScreenOffAnimationController = screenOffAnimationController;
 
         ShadeExpansionListener shadeExpansionListener = this::onPanelExpansionChanged;
@@ -901,10 +903,12 @@
         mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
 
         RegisterStatusBarResult result = null;
-        try {
-            result = mBarService.registerStatusBar(mCommandQueue);
-        } catch (RemoteException ex) {
-            ex.rethrowFromSystemServer();
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            try {
+                result = mBarService.registerStatusBar(mCommandQueue);
+            } catch (RemoteException ex) {
+                ex.rethrowFromSystemServer();
+            }
         }
 
         createAndAddWindows(result);
@@ -912,30 +916,45 @@
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
 
-        if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
-            mStatusBarModeRepository.getDefaultDisplay().showTransient();
-        }
-        mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
-                result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
-                result.mRequestedVisibleTypes, result.mPackageName, result.mLetterboxDetails);
-
-        // StatusBarManagerService has a back up of IME token and it's restored here.
-        mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeWindowVis,
-                result.mImeBackDisposition, result.mShowImeSwitcher);
-
-        // Set up the initial icon state
-        int numIcons = result.mIcons.size();
-        for (int i = 0; i < numIcons; i++) {
-            mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, String.format(
-                    "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
-                    numIcons,
-                    result.mDisabledFlags1,
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            if ((result.mTransientBarTypes & WindowInsets.Type.statusBars()) != 0) {
+                mStatusBarModeRepository.getDefaultDisplay().showTransient();
+            }
+            mCommandQueueCallbacks.onSystemBarAttributesChanged(
+                    mDisplayId,
                     result.mAppearance,
-                    result.mImeWindowVis));
+                    result.mAppearanceRegions,
+                    result.mNavbarColorManagedByIme,
+                    result.mBehavior,
+                    result.mRequestedVisibleTypes,
+                    result.mPackageName,
+                    result.mLetterboxDetails);
+
+            // StatusBarManagerService has a back up of IME token and it's restored here.
+            mCommandQueueCallbacks.setImeWindowStatus(
+                    mDisplayId,
+                    result.mImeWindowVis,
+                    result.mImeBackDisposition,
+                    result.mShowImeSwitcher);
+
+            // Set up the initial icon state
+            int numIcons = result.mIcons.size();
+            for (int i = 0; i < numIcons; i++) {
+                mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
+            }
+
+            if (DEBUG) {
+                Log.d(
+                        TAG,
+                        String.format(
+                                "init: icons=%d disabled=0x%08x lights=0x%08x imeButton=0x%08x",
+                                numIcons,
+                                result.mDisabledFlags1,
+                                result.mAppearance,
+                                result.mImeWindowVis));
+            }
         }
 
         IntentFilter internalFilter = new IntentFilter();
@@ -1005,24 +1024,30 @@
 
         mAccessibilityFloatingMenuController.init();
 
-        // set the initial view visibility
-        int disabledFlags1 = result.mDisabledFlags1;
-        int disabledFlags2 = result.mDisabledFlags2;
-        mInitController.addPostInitTask(() -> {
-            setUpDisableFlags(disabledFlags1, disabledFlags2);
-            try {
-                // NOTE(b/262059863): Force-update the disable flags after applying the flags
-                // returned from registerStatusBar(). The result's disabled flags may be stale
-                // if StatusBarManager's disabled flags are updated between registering the bar and
-                // this handling this post-init task. We force an update in this case, and use a new
-                // token to not conflict with any other disabled flags already requested by SysUI
-                Binder token = new Binder();
-                mBarService.disable(DISABLE_HOME, token, mContext.getPackageName());
-                mBarService.disable(0, token, mContext.getPackageName());
-            } catch (RemoteException ex) {
-                ex.rethrowFromSystemServer();
-            }
-        });
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            // set the initial view visibility
+            int disabledFlags1 = result.mDisabledFlags1;
+            int disabledFlags2 = result.mDisabledFlags2;
+            mInitController.addPostInitTask(
+                    () -> {
+                        setUpDisableFlags(disabledFlags1, disabledFlags2);
+                        try {
+                            // NOTE(b/262059863): Force-update the disable flags after applying the
+                            // flags returned from registerStatusBar(). The result's disabled flags
+                            // may be stale if StatusBarManager's disabled flags are updated between
+                            // registering the bar and this handling this post-init task. We force
+                            // an update in this case, and use a new token to not conflict with any
+                            // other disabled flags already requested by SysUI
+                            Binder token = new Binder();
+                            mBarService.disable(DISABLE_HOME, token, mContext.getPackageName());
+                            mBarService.disable(0, token, mContext.getPackageName());
+                        } catch (RemoteException ex) {
+                            ex.rethrowFromSystemServer();
+                        }
+                    });
+        }
 
         registerCallbacks();
 
@@ -1101,7 +1126,7 @@
 
     /**
      * @deprecated use {@link
-     * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
+     * WindowRootViewVisibilityInteractor#isLockscreenOrShadeVisible()} instead.
      */    @VisibleForTesting
     @Deprecated
     void initShadeVisibilityListener() {
@@ -1168,13 +1193,16 @@
         mWallpaperController.setRootView(getNotificationShadeWindowView());
 
         mDemoModeController.addCallback(mDemoModeCallback);
-        mJavaAdapter.alwaysCollectFlow(
-                mStatusBarModeRepository.getDefaultDisplay().isTransientShown(),
-                this::onTransientShownChanged);
-        mJavaAdapter.alwaysCollectFlow(
-                mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(),
-                this::updateBarMode);
-
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator.
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(
+                    mStatusBarModeRepository.getDefaultDisplay().isTransientShown(),
+                    this::onTransientShownChanged);
+            mJavaAdapter.alwaysCollectFlow(
+                    mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode(),
+                    this::updateBarMode);
+        }
         mCommandQueueCallbacks = mCommandQueueCallbacksLazy.get();
         mCommandQueue.addCallback(mCommandQueueCallbacks);
 
@@ -1184,59 +1212,70 @@
                 mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
         mWakeUpCoordinator.onPanelExpansionChanged(currentState);
 
-        // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
-        mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
-        mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
-
-        // Set up CollapsedStatusBarFragment and PhoneStatusBarView
-        mStatusBarInitializer.setStatusBarViewUpdatedListener(
-                (statusBarViewController, statusBarTransitions) -> {
-                    mPhoneStatusBarViewController = statusBarViewController;
-                    mStatusBarTransitions = statusBarTransitions;
-                    getNotificationShadeWindowViewController()
-                            .setStatusBarViewController(mPhoneStatusBarViewController);
-                    // Ensure we re-propagate panel expansion values to the panel controller and
-                    // any listeners it may have, such as PanelBar. This will also ensure we
-                    // re-display the notification panel if necessary (for example, if
-                    // a heads-up notification was being displayed and should continue being
-                    // displayed).
-                    mShadeSurface.updateExpansionAndVisibility();
-                    setBouncerShowingForStatusBarComponents(mBouncerShowing);
-                    checkBarModes();
-                });
-        // When the flag is on, we register the fragment as a core startable and this is not needed
+        // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in
+        // StatusBarOrchestrator.
         if (!StatusBarSimpleFragment.isEnabled()) {
+            // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
+            mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
+            mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
+
+            // Set up CollapsedStatusBarFragment and PhoneStatusBarView
+            mStatusBarInitializer.setStatusBarViewUpdatedListener(
+                    (statusBarViewController, statusBarTransitions) -> {
+
+                        mPhoneStatusBarViewController = statusBarViewController;
+                        mStatusBarTransitions = statusBarTransitions;
+                        getNotificationShadeWindowViewController()
+                                .setStatusBarViewController(mPhoneStatusBarViewController);
+                        // Ensure we re-propagate panel expansion values to the panel controller and
+                        // any listeners it may have, such as PanelBar. This will also ensure we
+                        // re-display the notification panel if necessary (for example, if
+                        // a heads-up notification was being displayed and should continue being
+                        // displayed).
+                        mShadeSurface.updateExpansionAndVisibility();
+                        setBouncerShowingForStatusBarComponents(mBouncerShowing);
+                        checkBarModes();
+                    });
+            // When the flag is on, we register the fragment as a core startable and this is not
+            // needed
             mStatusBarInitializer.initializeStatusBar();
         }
 
         mStatusBarTouchableRegionManager.setup(getNotificationShadeWindowView());
 
-        createNavigationBar(result);
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            createNavigationBar(result);
+        }
 
         mAmbientIndicationContainer = getNotificationShadeWindowView().findViewById(
                 R.id.ambient_indication_container);
 
-        mAutoHideController.setStatusBar(new AutoHideUiElement() {
-            @Override
-            public void synchronizeState() {
-                checkBarModes();
-            }
+        // When the StatusBarSimpleFragment flag is enabled, all this logic will be done in
+        // StatusBarOrchestrator.
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mAutoHideController.setStatusBar(
+                    new AutoHideUiElement() {
+                        @Override
+                        public void synchronizeState() {
+                            checkBarModes();
+                        }
 
-            @Override
-            public boolean shouldHideOnTouch() {
-                return !mRemoteInputManager.isRemoteInputActive();
-            }
+                        @Override
+                        public boolean shouldHideOnTouch() {
+                            return !mRemoteInputManager.isRemoteInputActive();
+                        }
 
-            @Override
-            public boolean isVisible() {
-                return isTransientShown();
-            }
+                        @Override
+                        public boolean isVisible() {
+                            return isTransientShown();
+                        }
 
-            @Override
-            public void hide() {
-                mStatusBarModeRepository.getDefaultDisplay().clearTransient();
-            }
-        });
+                        @Override
+                        public void hide() {
+                            mStatusBarModeRepository.getDefaultDisplay().clearTransient();
+                        }
+                    });
+        }
 
         ScrimView scrimBehind = getNotificationShadeWindowView().findViewById(R.id.scrim_behind);
         ScrimView notificationsScrim = getNotificationShadeWindowView()
@@ -1479,12 +1518,14 @@
      * @param state2 disable2 flags
      */
     protected void setUpDisableFlags(int state1, int state2) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
     }
 
     // TODO(b/117478341): This was left such that CarStatusBar can override this method.
     // Try to remove this.
     protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
+        StatusBarSimpleFragment.assertInLegacyMode();
         mNavigationBarController.createNavigationBars(true /* includeDefaultDisplay */, result);
     }
 
@@ -1697,14 +1738,16 @@
     @Override
     public void checkBarModes() {
         if (mDemoModeController.isInDemoMode()) return;
-        if (mStatusBarTransitions != null) {
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator.
+        if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) {
             checkBarMode(
                     mStatusBarModeRepository.getDefaultDisplay().getStatusBarMode().getValue(),
                     mStatusBarWindowState,
                     mStatusBarTransitions);
+            mNoAnimationOnNextBarModeChange = false;
         }
         mNavigationBarController.checkNavBarModes(mDisplayId);
-        mNoAnimationOnNextBarModeChange = false;
     }
 
     /** Temporarily hides Bubbles if the status bar is hidden. */
@@ -1728,7 +1771,9 @@
     }
 
     private void finishBarAnimations() {
-        if (mStatusBarTransitions != null) {
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator.
+        if (!StatusBarSimpleFragment.isEnabled() && mStatusBarTransitions != null) {
             mStatusBarTransitions.finishAnimations();
         }
         mNavigationBarController.finishBarAnimations(mDisplayId);
@@ -1770,14 +1815,17 @@
         }
 
         pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
-        pw.print("  mStatusBarWindowState=");
-        pw.println(windowStateToString(mStatusBarWindowState));
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            pw.print("  mStatusBarWindowState=");
+            pw.println(windowStateToString(mStatusBarWindowState));
+        }
         pw.print("  mDozing="); pw.println(mDozing);
         pw.print("  mWallpaperSupported= "); pw.println(mWallpaperSupported);
 
-        CentralSurfaces.dumpBarTransitions(
-                pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
-
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            CentralSurfaces.dumpBarTransitions(
+                    pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
+        }
         pw.println("  mMediaManager: ");
         if (mMediaManager != null) {
             mMediaManager.dump(pw, args);
@@ -1850,7 +1898,11 @@
     private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
         makeStatusBarView(result);
         mNotificationShadeWindowController.attach();
-        mStatusBarWindowController.attach();
+        // When the StatusBarSimpleFragment flag is enabled, this logic will be done in
+        // StatusBarOrchestrator
+        if (!StatusBarSimpleFragment.isEnabled()) {
+            mStatusBarWindowController.attach();
+        }
     }
 
     // called by makeStatusbar and also by PhoneStatusBarView
@@ -2475,7 +2527,7 @@
         int importance = bouncerShowing
                 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-        if (mPhoneStatusBarViewController != null) {
+        if (!StatusBarSimpleFragment.isEnabled() && mPhoneStatusBarViewController != null) {
             mPhoneStatusBarViewController.setImportantForAccessibility(importance);
         }
         mShadeSurface.setImportantForAccessibility(importance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 13b651e8..5b03198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -16,10 +16,20 @@
 package com.android.systemui.statusbar.phone.dagger
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.core.CommandQueueInitializer
 import com.android.systemui.statusbar.core.StatusBarInitializer
 import com.android.systemui.statusbar.core.StatusBarInitializerImpl
+import com.android.systemui.statusbar.core.StatusBarOrchestrator
+import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
 import dagger.Binds
+import dagger.Lazy
 import dagger.Module
+import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
 
@@ -27,6 +37,16 @@
 @Module
 interface StatusBarPhoneModule {
 
+    @Binds
+    abstract fun windowStateRepoStore(
+        impl: StatusBarWindowStateRepositoryStoreImpl
+    ): StatusBarWindowStateRepositoryStore
+
+    @Binds
+    abstract fun commandQCallbacks(
+        impl: CentralSurfacesCommandQueueCallbacks
+    ): CommandQueue.Callbacks
+
     /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
     @Binds
     @IntoMap
@@ -34,4 +54,34 @@
     fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable
 
     @Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(StatusBarOrchestrator::class)
+        fun orchestratorCoreStartable(
+            orchestratorLazy: Lazy<StatusBarOrchestrator>
+        ): CoreStartable {
+            return if (StatusBarSimpleFragment.isEnabled) {
+                orchestratorLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+
+        @Provides
+        @SysUISingleton
+        @IntoMap
+        @ClassKey(CommandQueueInitializer::class)
+        fun commandQueueInitializerCoreStartable(
+            initializerLazy: Lazy<CommandQueueInitializer>
+        ): CoreStartable {
+            return if (StatusBarSimpleFragment.isEnabled) {
+                initializerLazy.get()
+            } else {
+                CoreStartable.NOP
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
new file mode 100644
index 0000000..2a196c6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/CommandQueueInitializerTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import android.internal.statusbar.fakeStatusBarService
+import android.platform.test.annotations.EnableFlags
+import android.view.WindowInsets
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.initController
+import com.android.systemui.keyguard.data.repository.fakeCommandQueue
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockCommandQueueCallbacks
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommandQueueInitializerTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val initController = kosmos.initController
+    private val commandQueue = kosmos.fakeCommandQueue
+    private val commandQueueCallbacks = kosmos.mockCommandQueueCallbacks
+    private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
+    private val fakeStatusBarService = kosmos.fakeStatusBarService
+    private val initializer = kosmos.commandQueueInitializer
+
+    @Test
+    fun start_registersStatusBar() {
+        initializer.start()
+
+        assertThat(fakeStatusBarService.registeredStatusBar).isNotNull()
+    }
+
+    @Test
+    fun start_barResultHasTransientStatusBar_transientStateIsTrue() {
+        fakeStatusBarService.transientBarTypes = WindowInsets.Type.statusBars()
+
+        initializer.start()
+
+        assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isTrue()
+    }
+
+    @Test
+    fun start_barResultDoesNotHaveTransientStatusBar_transientStateIsFalse() {
+        fakeStatusBarService.transientBarTypes = WindowInsets.Type.navigationBars()
+
+        initializer.start()
+
+        assertThat(statusBarModeRepository.defaultDisplay.isTransientShown.value).isFalse()
+    }
+
+    @Test
+    fun start_callsOnSystemBarAttributesChanged_basedOnRegisterBarResult() {
+        initializer.start()
+
+        verify(commandQueueCallbacks)
+            .onSystemBarAttributesChanged(
+                context.displayId,
+                fakeStatusBarService.appearance,
+                fakeStatusBarService.appearanceRegions,
+                fakeStatusBarService.navbarColorManagedByIme,
+                fakeStatusBarService.behavior,
+                fakeStatusBarService.requestedVisibleTypes,
+                fakeStatusBarService.packageName,
+                fakeStatusBarService.letterboxDetails,
+            )
+    }
+
+    @Test
+    fun start_callsSetIcon_basedOnRegisterBarResult() {
+        initializer.start()
+
+        assertThat(commandQueue.icons).isEqualTo(fakeStatusBarService.statusBarIcons)
+    }
+
+    @Test
+    fun start_callsSetImeWindowStatus_basedOnRegisterBarResult() {
+        initializer.start()
+
+        verify(commandQueueCallbacks)
+            .setImeWindowStatus(
+                context.displayId,
+                fakeStatusBarService.imeWindowVis,
+                fakeStatusBarService.imeBackDisposition,
+                fakeStatusBarService.showImeSwitcher,
+            )
+    }
+
+    @Test
+    fun start_afterPostInitTaskExecuted_callsDisableFlags_basedOnRegisterBarResult() {
+        initializer.start()
+
+        initController.executePostInitTasks()
+
+        assertThat(commandQueue.disableFlags1ForDisplay(context.displayId))
+            .isEqualTo(fakeStatusBarService.disabledFlags1)
+        assertThat(commandQueue.disableFlags2ForDisplay(context.displayId))
+            .isEqualTo(fakeStatusBarService.disabledFlags2)
+    }
+
+    @Test
+    fun start_beforePostInitTaskExecuted_doesNotCallsDisableFlags() {
+        initializer.start()
+
+        assertThat(commandQueue.disableFlags1ForDisplay(context.displayId)).isNull()
+        assertThat(commandQueue.disableFlags2ForDisplay(context.displayId)).isNull()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
new file mode 100644
index 0000000..5803365
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import android.platform.test.annotations.EnableFlags
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.plugins.mockPluginDependencyProvider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.shade.mockNotificationShadeWindowViewController
+import com.android.systemui.shade.mockShadeSurface
+import com.android.systemui.statusbar.data.model.StatusBarMode
+import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT
+import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
+import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
+import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.testKosmos
+import com.android.wm.shell.bubbles.bubbles
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@EnableFlags(StatusBarSimpleFragment.FLAG_NAME)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarOrchestratorTest : SysuiTestCase() {
+
+    private val kosmos =
+        testKosmos().also {
+            it.testDispatcher = it.unconfinedTestDispatcher
+            it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
+        }
+    private val testScope = kosmos.testScope
+    private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
+    private val statusBarWindowController = kosmos.fakeStatusBarWindowController
+    private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
+    private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
+    private val notificationShadeWindowViewController =
+        kosmos.mockNotificationShadeWindowViewController
+    private val shadeSurface = kosmos.mockShadeSurface
+    private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
+    private val fakeStatusBarWindowStateRepositoryStore =
+        kosmos.fakeStatusBarWindowStateRepositoryStore
+    private val fakePowerRepository = kosmos.fakePowerRepository
+    private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
+    private val mockBubbles = kosmos.bubbles
+
+    private val orchestrator = kosmos.statusBarOrchestrator
+
+    @Test
+    fun start_setsUpPluginDependencies() {
+        orchestrator.start()
+
+        verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+        verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+    }
+
+    @Test
+    fun start_attachesWindow() {
+        orchestrator.start()
+
+        assertThat(statusBarWindowController.isAttached).isTrue()
+    }
+
+    @Test
+    fun start_setsStatusBarControllerOnShade() {
+        orchestrator.start()
+
+        verify(notificationShadeWindowViewController)
+            .setStatusBarViewController(statusBarViewController)
+    }
+
+    @Test
+    fun start_updatesShadeExpansion() {
+        orchestrator.start()
+
+        verify(shadeSurface).updateExpansionAndVisibility()
+    }
+
+    @Test
+    fun bouncerShowing_setsImportanceForA11yToNoHideDescendants() =
+        testScope.runTest {
+            orchestrator.start()
+
+            bouncerRepository.setPrimaryShow(isShowing = true)
+
+            verify(statusBarViewController)
+                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
+        }
+
+    @Test
+    fun bouncerNotShowing_setsImportanceForA11yToNoHideDescendants() =
+        testScope.runTest {
+            orchestrator.start()
+
+            bouncerRepository.setPrimaryShow(isShowing = false)
+
+            verify(statusBarViewController)
+                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
+        }
+
+    @Test
+    fun deviceGoesToSleep_barTransitionsAnimationsAreFinished() =
+        testScope.runTest {
+            putDeviceToSleep()
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions).finishAnimations()
+        }
+
+    @Test
+    fun deviceIsAwake_barTransitionsAnimationsAreNotFinished() =
+        testScope.runTest {
+            awakeDevice()
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+        }
+
+    @Test
+    fun statusBarVisible_notifiesBubbles() =
+        testScope.runTest {
+            setStatusBarMode(TRANSPARENT)
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+
+            orchestrator.start()
+
+            verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ true)
+        }
+
+    @Test
+    fun statusBarInLightsOutMode_notifiesBubblesWithStatusBarInvisible() =
+        testScope.runTest {
+            setStatusBarMode(LIGHTS_OUT)
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+
+            orchestrator.start()
+
+            verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+        }
+
+    @Test
+    fun statusBarInLightsOutTransparentMode_notifiesBubblesWithStatusBarInvisible() =
+        testScope.runTest {
+            setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+
+            orchestrator.start()
+
+            verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+        }
+
+    @Test
+    fun statusBarWindowNotShowing_notifiesBubblesWithStatusBarInvisible() =
+        testScope.runTest {
+            setStatusBarMode(TRANSPARENT)
+            setStatusBarWindowState(StatusBarWindowState.Hidden)
+
+            orchestrator.start()
+
+            verify(mockBubbles).onStatusBarVisibilityChanged(/* visible= */ false)
+        }
+
+    @Test
+    fun statusBarModeChange_transitionsToModeWithAnimation() =
+        testScope.runTest {
+            awakeDevice()
+            clearTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+        }
+
+    @Test
+    fun statusBarModeChange_keepsTransitioningAsModeChanges() =
+        testScope.runTest {
+            awakeDevice()
+            clearTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+
+            setStatusBarMode(OPAQUE)
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
+
+            setStatusBarMode(LIGHTS_OUT)
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
+
+            setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+        }
+
+    @Test
+    fun statusBarModeChange_transientIsShown_transitionsToModeWithoutAnimation() =
+        testScope.runTest {
+            awakeDevice()
+            setTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+        }
+
+    @Test
+    fun statusBarModeChange_windowIsHidden_transitionsToModeWithoutAnimation() =
+        testScope.runTest {
+            awakeDevice()
+            clearTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Hidden)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+        }
+
+    @Test
+    fun statusBarModeChange_deviceIsAsleep_transitionsToModeWithoutAnimation() =
+        testScope.runTest {
+            putDeviceToSleep()
+            clearTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            verify(mockPhoneStatusBarTransitions)
+                .transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
+        }
+
+    @Test
+    fun statusBarModeAnimationConditionsChange_withoutBarModeChange_noNewTransitionsHappen() =
+        testScope.runTest {
+            awakeDevice()
+            clearTransientStatusBar()
+            setStatusBarWindowState(StatusBarWindowState.Showing)
+            setStatusBarMode(TRANSPARENT)
+
+            orchestrator.start()
+
+            putDeviceToSleep()
+            awakeDevice()
+            setTransientStatusBar()
+            clearTransientStatusBar()
+
+            verify(mockPhoneStatusBarTransitions, times(1))
+                .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
+        }
+
+    private fun putDeviceToSleep() {
+        fakePowerRepository.updateWakefulness(
+            rawState = WakefulnessState.ASLEEP,
+            lastWakeReason = WakeSleepReason.KEY,
+            lastSleepReason = WakeSleepReason.KEY,
+            powerButtonLaunchGestureTriggered = true,
+        )
+    }
+
+    private fun awakeDevice() {
+        fakePowerRepository.updateWakefulness(
+            rawState = WakefulnessState.AWAKE,
+            lastWakeReason = WakeSleepReason.KEY,
+            lastSleepReason = WakeSleepReason.KEY,
+            powerButtonLaunchGestureTriggered = true,
+        )
+    }
+
+    private fun setTransientStatusBar() {
+        statusBarModeRepository.defaultDisplay.showTransient()
+    }
+
+    private fun clearTransientStatusBar() {
+        statusBarModeRepository.defaultDisplay.clearTransient()
+    }
+
+    private fun setStatusBarWindowState(state: StatusBarWindowState) {
+        fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+    }
+
+    private fun setStatusBarMode(statusBarMode: StatusBarMode) {
+        statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c710c56..15ea811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -169,6 +169,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
+import com.android.systemui.statusbar.core.StatusBarOrchestrator;
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -346,6 +347,7 @@
     @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
     @Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
     @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+    @Mock private StatusBarOrchestrator mStatusBarOrchestrator;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
new file mode 100644
index 0000000..cc0597b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/FakeStatusBarService.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2024 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 android.internal.statusbar
+
+import android.app.Notification
+import android.content.ComponentName
+import android.graphics.Rect
+import android.graphics.drawable.Icon
+import android.hardware.biometrics.IBiometricContextListener
+import android.hardware.biometrics.IBiometricSysuiReceiver
+import android.hardware.biometrics.PromptInfo
+import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback
+import android.media.INearbyMediaDevicesProvider
+import android.media.MediaRoute2Info
+import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.os.UserHandle
+import android.util.ArrayMap
+import android.view.KeyEvent
+import com.android.internal.logging.InstanceId
+import com.android.internal.statusbar.IAddTileResultCallback
+import com.android.internal.statusbar.ISessionListener
+import com.android.internal.statusbar.IStatusBar
+import com.android.internal.statusbar.IStatusBarService
+import com.android.internal.statusbar.IUndoMediaTransferCallback
+import com.android.internal.statusbar.LetterboxDetails
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.internal.statusbar.RegisterStatusBarResult
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.internal.view.AppearanceRegion
+import org.mockito.kotlin.mock
+
+class FakeStatusBarService : IStatusBarService.Stub() {
+
+    var registeredStatusBar: IStatusBar? = null
+        private set
+
+    var statusBarIcons =
+        ArrayMap<String, StatusBarIcon>().also {
+            it["slot1"] = mock<StatusBarIcon>()
+            it["slot2"] = mock<StatusBarIcon>()
+        }
+    var disabledFlags1 = 1234567
+    var appearance = 123
+    var appearanceRegions =
+        arrayOf(
+            AppearanceRegion(
+                /* appearance = */ 123,
+                /* bounds = */ Rect(/* left= */ 4, /* top= */ 3, /* right= */ 2, /* bottom= */ 1),
+            ),
+            AppearanceRegion(
+                /* appearance = */ 345,
+                /* bounds = */ Rect(/* left= */ 1, /* top= */ 2, /* right= */ 3, /* bottom= */ 4),
+            ),
+        )
+    var imeWindowVis = 987
+    var imeBackDisposition = 654
+    var showImeSwitcher = true
+    var disabledFlags2 = 7654321
+    var navbarColorManagedByIme = true
+    var behavior = 234
+    var requestedVisibleTypes = 345
+    var packageName = "fake.bar.ser.vice"
+    var transientBarTypes = 0
+    var letterboxDetails =
+        arrayOf(
+            LetterboxDetails(
+                /* letterboxInnerBounds = */ Rect(
+                    /* left= */ 5,
+                    /* top= */ 6,
+                    /* right= */ 7,
+                    /* bottom= */ 8,
+                ),
+                /* letterboxFullBounds = */ Rect(
+                    /* left= */ 1,
+                    /* top= */ 2,
+                    /* right= */ 3,
+                    /* bottom= */ 4,
+                ),
+                /* appAppearance = */ 123,
+            )
+        )
+
+    override fun expandNotificationsPanel() {}
+
+    override fun collapsePanels() {}
+
+    override fun togglePanel() {}
+
+    override fun disable(what: Int, token: IBinder, pkg: String) {
+        disableForUser(what, token, pkg, userId = 0)
+    }
+
+    override fun disableForUser(what: Int, token: IBinder, pkg: String, userId: Int) {}
+
+    override fun disable2(what: Int, token: IBinder, pkg: String) {
+        disable2ForUser(what, token, pkg, userId = 0)
+    }
+
+    override fun disable2ForUser(what: Int, token: IBinder, pkg: String, userId: Int) {}
+
+    override fun getDisableFlags(token: IBinder, userId: Int): IntArray {
+        return intArrayOf(disabledFlags1, disabledFlags2)
+    }
+
+    override fun setIcon(
+        slot: String,
+        iconPackage: String,
+        iconId: Int,
+        iconLevel: Int,
+        contentDescription: String,
+    ) {}
+
+    override fun setIconVisibility(slot: String, visible: Boolean) {}
+
+    override fun removeIcon(slot: String) {}
+
+    override fun setImeWindowStatus(
+        displayId: Int,
+        vis: Int,
+        backDisposition: Int,
+        showImeSwitcher: Boolean,
+    ) {}
+
+    override fun expandSettingsPanel(subPanel: String) {}
+
+    override fun registerStatusBar(callbacks: IStatusBar): RegisterStatusBarResult {
+        registeredStatusBar = callbacks
+        return RegisterStatusBarResult(
+            statusBarIcons,
+            disabledFlags1,
+            appearance,
+            appearanceRegions,
+            imeWindowVis,
+            imeBackDisposition,
+            showImeSwitcher,
+            disabledFlags2,
+            navbarColorManagedByIme,
+            behavior,
+            requestedVisibleTypes,
+            packageName,
+            transientBarTypes,
+            letterboxDetails,
+        )
+    }
+
+    override fun onPanelRevealed(clearNotificationEffects: Boolean, numItems: Int) {}
+
+    override fun onPanelHidden() {}
+
+    override fun clearNotificationEffects() {}
+
+    override fun onNotificationClick(key: String, nv: NotificationVisibility) {}
+
+    override fun onNotificationActionClick(
+        key: String,
+        actionIndex: Int,
+        action: Notification.Action,
+        nv: NotificationVisibility,
+        generatedByAssistant: Boolean,
+    ) {}
+
+    override fun onNotificationError(
+        pkg: String,
+        tag: String,
+        id: Int,
+        uid: Int,
+        initialPid: Int,
+        message: String,
+        userId: Int,
+    ) {}
+
+    override fun onClearAllNotifications(userId: Int) {}
+
+    override fun onNotificationClear(
+        pkg: String,
+        userId: Int,
+        key: String,
+        dismissalSurface: Int,
+        dismissalSentiment: Int,
+        nv: NotificationVisibility,
+    ) {}
+
+    override fun onNotificationVisibilityChanged(
+        newlyVisibleKeys: Array<NotificationVisibility>,
+        noLongerVisibleKeys: Array<NotificationVisibility>,
+    ) {}
+
+    override fun onNotificationExpansionChanged(
+        key: String,
+        userAction: Boolean,
+        expanded: Boolean,
+        notificationLocation: Int,
+    ) {}
+
+    override fun onNotificationDirectReplied(key: String) {}
+
+    override fun onNotificationSmartSuggestionsAdded(
+        key: String,
+        smartReplyCount: Int,
+        smartActionCount: Int,
+        generatedByAssistant: Boolean,
+        editBeforeSending: Boolean,
+    ) {}
+
+    override fun onNotificationSmartReplySent(
+        key: String,
+        replyIndex: Int,
+        reply: CharSequence,
+        notificationLocation: Int,
+        modifiedBeforeSending: Boolean,
+    ) {}
+
+    override fun onNotificationSettingsViewed(key: String) {}
+
+    override fun onNotificationBubbleChanged(key: String, isBubble: Boolean, flags: Int) {}
+
+    override fun onBubbleMetadataFlagChanged(key: String, flags: Int) {}
+
+    override fun hideCurrentInputMethodForBubbles(displayId: Int) {}
+
+    override fun grantInlineReplyUriPermission(
+        key: String,
+        uri: Uri,
+        user: UserHandle,
+        packageName: String,
+    ) {}
+
+    override fun clearInlineReplyUriPermissions(key: String) {}
+
+    override fun onNotificationFeedbackReceived(key: String, feedback: Bundle) {}
+
+    override fun onGlobalActionsShown() {}
+
+    override fun onGlobalActionsHidden() {}
+
+    override fun shutdown() {}
+
+    override fun reboot(safeMode: Boolean) {}
+
+    override fun restart() {}
+
+    override fun addTile(tile: ComponentName) {}
+
+    override fun remTile(tile: ComponentName) {}
+
+    override fun clickTile(tile: ComponentName) {}
+
+    override fun handleSystemKey(key: KeyEvent) {}
+
+    override fun getLastSystemKey(): Int {
+        return -1
+    }
+
+    override fun showPinningEnterExitToast(entering: Boolean) {}
+
+    override fun showPinningEscapeToast() {}
+
+    override fun showAuthenticationDialog(
+        promptInfo: PromptInfo,
+        sysuiReceiver: IBiometricSysuiReceiver,
+        sensorIds: IntArray,
+        credentialAllowed: Boolean,
+        requireConfirmation: Boolean,
+        userId: Int,
+        operationId: Long,
+        opPackageName: String,
+        requestId: Long,
+    ) {}
+
+    override fun onBiometricAuthenticated(modality: Int) {}
+
+    override fun onBiometricHelp(modality: Int, message: String) {}
+
+    override fun onBiometricError(modality: Int, error: Int, vendorCode: Int) {}
+
+    override fun hideAuthenticationDialog(requestId: Long) {}
+
+    override fun setBiometicContextListener(listener: IBiometricContextListener) {}
+
+    override fun setUdfpsRefreshRateCallback(callback: IUdfpsRefreshRateRequestCallback) {}
+
+    override fun showInattentiveSleepWarning() {}
+
+    override fun dismissInattentiveSleepWarning(animated: Boolean) {}
+
+    override fun startTracing() {}
+
+    override fun stopTracing() {}
+
+    override fun isTracing(): Boolean {
+        return false
+    }
+
+    override fun suppressAmbientDisplay(suppress: Boolean) {}
+
+    override fun requestTileServiceListeningState(componentName: ComponentName, userId: Int) {}
+
+    override fun requestAddTile(
+        componentName: ComponentName,
+        label: CharSequence,
+        icon: Icon,
+        userId: Int,
+        callback: IAddTileResultCallback,
+    ) {}
+
+    override fun cancelRequestAddTile(packageName: String) {}
+
+    override fun setNavBarMode(navBarMode: Int) {}
+
+    override fun getNavBarMode(): Int {
+        return -1
+    }
+
+    override fun registerSessionListener(sessionFlags: Int, listener: ISessionListener) {}
+
+    override fun unregisterSessionListener(sessionFlags: Int, listener: ISessionListener) {}
+
+    override fun onSessionStarted(sessionType: Int, instanceId: InstanceId) {}
+
+    override fun onSessionEnded(sessionType: Int, instanceId: InstanceId) {}
+
+    override fun updateMediaTapToTransferSenderDisplay(
+        displayState: Int,
+        routeInfo: MediaRoute2Info,
+        undoCallback: IUndoMediaTransferCallback,
+    ) {}
+
+    override fun updateMediaTapToTransferReceiverDisplay(
+        displayState: Int,
+        routeInfo: MediaRoute2Info,
+        appIcon: Icon,
+        appName: CharSequence,
+    ) {}
+
+    override fun registerNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
+
+    override fun unregisterNearbyMediaDevicesProvider(provider: INearbyMediaDevicesProvider) {}
+
+    override fun showRearDisplayDialog(currentBaseState: Int) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt b/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt
new file mode 100644
index 0000000..1304161
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/internal/statusbar/StatusBarServiceKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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 android.internal.statusbar
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeStatusBarService by Kosmos.Fixture { FakeStatusBarService() }
+
+var Kosmos.statusBarService by Kosmos.Fixture { fakeStatusBarService }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt
new file mode 100644
index 0000000..39384fd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/DemoModeKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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
+
+import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockDemoModeController by Kosmos.Fixture { mock<DemoModeController>() }
+
+var Kosmos.demoModeController by Kosmos.Fixture { mockDemoModeController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt
new file mode 100644
index 0000000..13169e1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/InitControllerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.initController by Kosmos.Fixture { InitController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
index 3a59f6a..601c145 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
@@ -18,6 +18,8 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.content.Context
+import androidx.collection.ArrayMap
+import com.android.internal.statusbar.StatusBarIcon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.CommandQueue
@@ -31,6 +33,11 @@
     CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) {
     private val callbacks = mutableListOf<Callbacks>()
 
+    val icons = ArrayMap<String, StatusBarIcon>()
+
+    private val perDisplayDisableFlags1 = mutableMapOf<Int, Int>()
+    private val perDisplayDisableFlags2 = mutableMapOf<Int, Int>()
+
     override fun addCallback(callback: Callbacks) {
         callbacks.add(callback)
     }
@@ -44,6 +51,23 @@
     }
 
     fun callbackCount(): Int = callbacks.size
+
+    override fun setIcon(slot: String, icon: StatusBarIcon) {
+        icons[slot] = icon
+    }
+
+    override fun disable(displayId: Int, state1: Int, state2: Int, animate: Boolean) {
+        perDisplayDisableFlags1[displayId] = state1
+        perDisplayDisableFlags2[displayId] = state2
+    }
+
+    override fun disable(displayId: Int, state1: Int, state2: Int) {
+        disable(displayId, state1, state2, /* animate= */ false)
+    }
+
+    fun disableFlags1ForDisplay(displayId: Int) = perDisplayDisableFlags1[displayId]
+
+    fun disableFlags2ForDisplay(displayId: Int) = perDisplayDisableFlags2[displayId]
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt
new file mode 100644
index 0000000..9e2039e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationBarControllerKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.navigationbar
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockNavigationBarController by Kosmos.Fixture { mock<NavigationBarController>() }
+
+var Kosmos.navigationBarController by Kosmos.Fixture { mockNavigationBarController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt
new file mode 100644
index 0000000..f1388e9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/PluginDependencyKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.plugins
+
+import android.testing.LeakCheck
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.utils.leaks.FakePluginManager
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.mock
+
+val Kosmos.leakCheck by Kosmos.Fixture { LeakCheck() }
+
+val Kosmos.fakePluginManager by Kosmos.Fixture { FakePluginManager(leakCheck) }
+
+var Kosmos.pluginManager by Kosmos.Fixture { fakePluginManager }
+
+val Kosmos.pluginDependencyProvider by Kosmos.Fixture { PluginDependencyProvider { pluginManager } }
+
+val Kosmos.mockPluginDependencyProvider by Kosmos.Fixture { mock<PluginDependencyProvider>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
index 1ceab68..a9f9c82 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeViewControllerKosmos.kt
@@ -20,3 +20,13 @@
 import com.android.systemui.util.mockito.mock
 
 var Kosmos.shadeViewController by Kosmos.Fixture { mock<ShadeViewController>() }
+
+val Kosmos.mockNotificationShadeWindowViewController by
+    Kosmos.Fixture { mock<NotificationShadeWindowViewController>() }
+
+var Kosmos.notificationShadeWindowViewController by
+    Kosmos.Fixture { mockNotificationShadeWindowViewController }
+
+val Kosmos.mockShadeSurface by Kosmos.Fixture { mock<ShadeSurface>() }
+
+var Kosmos.shadeSurface by Kosmos.Fixture { mockShadeSurface }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
index 27f7f68..f571c1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt
@@ -19,4 +19,10 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
 
-var Kosmos.commandQueue by Kosmos.Fixture { mock<CommandQueue>() }
+val Kosmos.mockCommandQueue by Kosmos.Fixture { mock<CommandQueue>() }
+
+var Kosmos.commandQueue by Kosmos.Fixture { mockCommandQueue }
+
+val Kosmos.mockCommandQueueCallbacks by Kosmos.Fixture { mock<CommandQueue.Callbacks>() }
+
+var Kosmos.commandQueueCallbacks by Kosmos.Fixture { mockCommandQueue }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
index 554bdbe..d436cd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/NotificationRemoteInputManagerKosmos.kt
@@ -19,5 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
 
-var Kosmos.notificationRemoteInputManager by
+val Kosmos.mockNotificationRemoteInputManager by
     Kosmos.Fixture { mock<NotificationRemoteInputManager>() }
+
+var Kosmos.notificationRemoteInputManager by Kosmos.Fixture { mockNotificationRemoteInputManager }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt
new file mode 100644
index 0000000..cba4e8e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/CommandQueueInitializerKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import android.content.testableContext
+import android.internal.statusbar.fakeStatusBarService
+import com.android.systemui.initController
+import com.android.systemui.keyguard.data.repository.fakeCommandQueue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.navigationbar.mockNavigationBarController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockCommandQueueCallbacks
+
+var Kosmos.commandQueueInitializer by
+    Kosmos.Fixture {
+        CommandQueueInitializer(
+            testableContext,
+            fakeCommandQueue,
+            { mockCommandQueueCallbacks },
+            fakeStatusBarModeRepository,
+            initController,
+            fakeStatusBarService,
+            mockNavigationBarController,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
new file mode 100644
index 0000000..edd6604
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
+import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+
+class FakeStatusBarInitializer(
+    private val statusBarViewController: PhoneStatusBarViewController,
+    private val statusBarTransitions: PhoneStatusBarTransitions,
+) : StatusBarInitializer {
+
+    override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
+        set(value) {
+            field = value
+            value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
+        }
+
+    override fun initializeStatusBar() {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
new file mode 100644
index 0000000..d103200
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
+import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+
+val Kosmos.fakeStatusBarInitializer by
+    Kosmos.Fixture {
+        FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
+    }
+
+var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
new file mode 100644
index 0000000..c53e44d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.core
+
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.mockDemoModeController
+import com.android.systemui.plugins.mockPluginDependencyProvider
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.shade.mockNotificationShadeWindowViewController
+import com.android.systemui.shade.mockShadeSurface
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.mockNotificationRemoteInputManager
+import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.wm.shell.bubbles.bubblesOptional
+
+val Kosmos.statusBarOrchestrator by
+    Kosmos.Fixture {
+        StatusBarOrchestrator(
+            applicationCoroutineScope,
+            fakeStatusBarInitializer,
+            fakeStatusBarWindowController,
+            fakeStatusBarModeRepository,
+            mockDemoModeController,
+            mockPluginDependencyProvider,
+            mockAutoHideController,
+            mockNotificationRemoteInputManager,
+            { mockNotificationShadeWindowViewController },
+            mockShadeSurface,
+            bubblesOptional,
+            statusBarWindowStateRepositoryStore,
+            powerInteractor,
+            primaryBouncerInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
new file mode 100644
index 0000000..090ce31
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.phone
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.mockAutoHideController by Kosmos.Fixture { mock<AutoHideController>() }
+
+var Kosmos.autoHideController by Kosmos.Fixture { mockAutoHideController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt
new file mode 100644
index 0000000..603ee08
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/PhoneStatusBarKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.phone
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.Mockito.mock
+
+val Kosmos.mockPhoneStatusBarViewController: PhoneStatusBarViewController by
+    Kosmos.Fixture { mock(PhoneStatusBarViewController::class.java) }
+
+var Kosmos.phoneStatusBarViewController by Kosmos.Fixture { mockPhoneStatusBarViewController }
+
+val Kosmos.mockPhoneStatusBarTransitions: PhoneStatusBarTransitions by
+    Kosmos.Fixture { mock(PhoneStatusBarTransitions::class.java) }
+
+var Kosmos.phoneStatusBarTransitions by Kosmos.Fixture { mockPhoneStatusBarTransitions }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
new file mode 100644
index 0000000..528c9d9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowController.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.window
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.fragments.FragmentHostManager
+import java.util.Optional
+
+class FakeStatusBarWindowController : StatusBarWindowController {
+
+    var isAttached = false
+        private set
+
+    override val statusBarHeight: Int = 0
+
+    override fun refreshStatusBarHeight() {}
+
+    override fun attach() {
+        isAttached = true
+    }
+
+    override fun addViewToWindow(view: View, layoutParams: ViewGroup.LayoutParams) {}
+
+    override val backgroundView: View
+        get() = throw NotImplementedError()
+
+    override val fragmentHostManager: FragmentHostManager
+        get() = throw NotImplementedError()
+
+    override fun wrapAnimationControllerIfInStatusBar(
+        rootView: View,
+        animationController: ActivityTransitionAnimator.Controller,
+    ): Optional<ActivityTransitionAnimator.Controller> = Optional.empty()
+
+    override fun setForceStatusBarVisible(forceStatusBarVisible: Boolean) {}
+
+    override fun setOngoingProcessRequiresStatusBarVisible(visible: Boolean) {}
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
new file mode 100644
index 0000000..c198b35
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.window
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() }
+
+var Kosmos.statusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
new file mode 100644
index 0000000..6532a7e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.window.data.repository
+
+import android.view.Display
+import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeStatusBarWindowStateRepositoryStore : StatusBarWindowStateRepositoryStore {
+
+    private val perDisplayRepos = mutableMapOf<Int, FakeStatusBarWindowStatePerDisplayRepository>()
+
+    override val defaultDisplay: FakeStatusBarWindowStatePerDisplayRepository =
+        forDisplay(Display.DEFAULT_DISPLAY)
+
+    override fun forDisplay(displayId: Int): FakeStatusBarWindowStatePerDisplayRepository =
+        perDisplayRepos.computeIfAbsent(displayId) {
+            FakeStatusBarWindowStatePerDisplayRepository()
+        }
+}
+
+class FakeStatusBarWindowStatePerDisplayRepository : StatusBarWindowStatePerDisplayRepository {
+
+    private val _windowState = MutableStateFlow(StatusBarWindowState.Hidden)
+
+    override val windowState = _windowState.asStateFlow()
+
+    fun setWindowState(state: StatusBarWindowState) {
+        _windowState.value = state
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index e2b7f5f..2205a3b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.statusbar.commandQueue
 
+val Kosmos.fakeStatusBarWindowStateRepositoryStore by
+    Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
+
 class KosmosStatusBarWindowStatePerDisplayRepositoryFactory(private val kosmos: Kosmos) :
     StatusBarWindowStatePerDisplayRepositoryFactory {
     override fun create(displayId: Int): StatusBarWindowStatePerDisplayRepositoryImpl {
@@ -32,7 +35,7 @@
     }
 }
 
-val Kosmos.statusBarWindowStateRepositoryStore by
+var Kosmos.statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore by
     Kosmos.Fixture {
         StatusBarWindowStateRepositoryStoreImpl(
             displayId = displayTracker.defaultDisplayId,