Use WindowManager callback for add/remove system decorations in DisplayRepository.

Bug: 412580790
Test: DisplayRepositoryTest
Flag: com.android.window.flags.enable_sys_decors_callbacks_via_wm
Change-Id: Ib7a9285e1becf14647e106f8e525de0ea515a07f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 388ceb0..848c6c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -18,11 +18,14 @@
 
 import android.hardware.display.DisplayManager
 import android.os.fakeHandler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.view.Display
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.Display.TYPE_EXTERNAL
 import android.view.Display.TYPE_INTERNAL
+import android.view.IDisplayWindowListener
 import android.view.mockIWindowManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -37,6 +40,7 @@
 import com.android.systemui.statusbar.mockCommandQueue
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.window.flags.Flags
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -48,6 +52,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.whenever
 
@@ -64,6 +69,7 @@
     private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
     private val commandQueueCallbacks = kotlinArgumentCaptor<CommandQueue.Callbacks>()
     private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
+    private lateinit var wmListener: IDisplayWindowListener
 
     private val testHandler = kosmos.fakeHandler
     private val testScope = kosmos.testScope
@@ -517,6 +523,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_onStart_emitsDisplaysWithSystemDecorations() =
         testScope.runTest {
             setDisplays(0, 1, 2)
@@ -530,6 +537,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsIncludingNewDisplayIds() =
         testScope.runTest {
             setDisplays(0)
@@ -543,6 +551,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsToNewSubscribers() =
         testScope.runTest {
             setDisplays(0)
@@ -558,6 +567,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_systemDecorationRemoved_doesNotEmitRemovedDisplayId() =
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -570,6 +580,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_systemDecorationsRemoved_nonExistentDisplay_noEffect() =
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -581,6 +592,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_displayRemoved_doesNotEmitRemovedDisplayId() =
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -593,6 +605,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_displayRemoved_nonExistentDisplay_noEffect() =
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -604,6 +617,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_onFlowCollection_commandQueueCallbackRegistered() =
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -614,6 +628,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
     fun displayIdsWithSystemDecorations_afterFlowCollection_commandQueueCallbackUnregistered() {
         testScope.runTest {
             val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
@@ -626,6 +641,100 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_systemDecorationAdded_emitsIncludingNewDisplayIds() =
+        testScope.runTest {
+            setDisplays(0)
+            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureWmListener()
+
+            wmListener.onDisplayAddSystemDecorations(2)
+            wmListener.onDisplayAddSystemDecorations(3)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 2, 3)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_systemDecorationAdded_emitsToNewSubscribers() =
+        testScope.runTest {
+            setDisplays(0)
+            whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+
+            val priorDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureWmListener()
+            wmListener.onDisplayAddSystemDecorations(1)
+            assertThat(priorDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_systemDecorationAdded_doesNotEmitRemovedDisplayId() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureWmListener()
+
+            wmListener.onDisplayAddSystemDecorations(1)
+            wmListener.onDisplayAddSystemDecorations(2)
+            wmListener.onDisplayRemoveSystemDecorations(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_systemDecorationsRemoved_nonExistentDisplay_noEffect() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureWmListener()
+
+            wmListener.onDisplayAddSystemDecorations(1)
+            wmListener.onDisplayRemoveSystemDecorations(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_displayRemoved_doesNotEmitRemovedDisplayId() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureAddedRemovedListener()
+            captureWmListener()
+
+            wmListener.onDisplayAddSystemDecorations(1)
+            wmListener.onDisplayAddSystemDecorations(2)
+            sendOnDisplayRemoved(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_SYS_DECORS_CALLBACKS_VIA_WM)
+    fun displayIdsWithSystemDecorations_withWmCallback_displayRemoved_nonExistentDisplay_noEffect() =
+        testScope.runTest {
+            val lastDisplayIdsWithSystemDecorations by
+                collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+            captureAddedRemovedListener()
+            captureWmListener()
+
+            wmListener.onDisplayAddSystemDecorations(1)
+            sendOnDisplayRemoved(2)
+
+            assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+        }
+
+    @Test
     fun getDisplay_slowMappingToDisplay_returnsRegardless() =
         testScope.runTest {
             val displayIds by collectLastValue(displayRepository.displayIds)
@@ -766,4 +875,10 @@
         val idsToSet = ids.toSet() + DEFAULT_DISPLAY
         setDisplays(idsToSet.map { display(type = TYPE_EXTERNAL, id = it) })
     }
+
+    private fun captureWmListener() {
+        val captor = argumentCaptor<IDisplayWindowListener>()
+        verify(windowManager).registerDisplayWindowListener(captor.capture())
+        wmListener = captor.firstValue
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index 5a9f919..d37a4a2 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -18,20 +18,22 @@
 
 import android.hardware.display.DisplayManager
 import android.os.Handler
+import android.view.IWindowManager
 import com.android.app.displaylib.DisplayLibBackground
 import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.DisplaysWithDecorationsRepository
 import com.android.app.displaylib.PerDisplayRepository
 import com.android.app.displaylib.createDisplayLibComponent
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayLib
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
 import com.android.systemui.display.data.repository.DisplayRepository
 import com.android.systemui.display.data.repository.DisplayRepositoryImpl
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
 import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
-import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepository
 import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepositoryImpl
 import com.android.systemui.display.data.repository.FocusedDisplayRepository
 import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
@@ -123,12 +125,14 @@
     @SysUISingleton
     fun displayLibComponent(
         displayManager: DisplayManager,
+        windowManager: IWindowManager,
         @Background backgroundHandler: Handler,
         @Background bgApplicationScope: CoroutineScope,
         @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
     ): DisplayLibComponent {
         return createDisplayLibComponent(
             displayManager,
+            windowManager,
             backgroundHandler,
             bgApplicationScope,
             backgroundCoroutineDispatcher,
@@ -142,4 +146,13 @@
     ): com.android.app.displaylib.DisplayRepository {
         return displayLibComponent.displayRepository
     }
+
+    @Provides
+    @SysUISingleton
+    @DisplayLib
+    fun providesDisplaysWithDecorationsRepositoryFromLib(
+        displayLibComponent: DisplayLibComponent
+    ): DisplaysWithDecorationsRepository {
+        return displayLibComponent.displaysWithDecorationsRepository
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt
index c83b99c..cd53751 100644
--- a/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt
@@ -54,4 +54,10 @@
 
     /** Annotates the display id inside the subcomponent. */
     @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayId
+
+    /**
+     * Annotates the displaylib implementation of a class.
+     * TODO(b/408503553): Remove this annotation once the flag is cleaned up.
+     */
+    @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayLib
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 01bbf2d..9e72fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -17,7 +17,10 @@
 package com.android.systemui.display.data.repository
 
 import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
+import com.android.app.displaylib.DisplaysWithDecorationsRepository
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayLib
+import com.android.window.flags.Flags.enableSysDecorsCallbacksViaWm
 import javax.inject.Inject
 
 /**
@@ -34,7 +37,13 @@
 constructor(
     private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
     private val displaysWithDecorationsRepositoryImpl: DisplaysWithDecorationsRepository,
+    @DisplayLib
+    private val displaysWithDecorationsRepositoryImplFromLib: DisplaysWithDecorationsRepository,
 ) :
     DisplayRepositoryFromLib by displayRepositoryFromLib,
-    DisplaysWithDecorationsRepository by displaysWithDecorationsRepositoryImpl,
+    DisplaysWithDecorationsRepository by (if (enableSysDecorsCallbacksViaWm()) {
+        displaysWithDecorationsRepositoryImplFromLib
+    } else {
+        displaysWithDecorationsRepositoryImpl
+    }),
     DisplayRepository
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepositoryImpl.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt
rename to packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepositoryImpl.kt
index f4a2ed4..cc3081c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepositoryImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.display.data.repository
 
 import android.view.IWindowManager
+import com.android.app.displaylib.DisplaysWithDecorationsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.CommandQueue
@@ -33,12 +34,6 @@
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.stateIn
 
-/** Provides the displays with decorations. */
-interface DisplaysWithDecorationsRepository {
-    /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
-    val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-}
-
 @SysUISingleton
 class DisplaysWithDecorationsRepositoryImpl
 @Inject
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayRepositoryKosmos.kt
index ce1d06d3..bcb6633 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayRepositoryKosmos.kt
@@ -76,7 +76,18 @@
         displayRepositoryFromDisplayLib,
     )
 }
+val Kosmos.displaysWithDecorationsRepositoryFromDisplayLib by Fixture {
+    com.android.app.displaylib.DisplaysWithDecorationsRepositoryImpl(
+        mockIWindowManager,
+        testScope.backgroundScope,
+        displayRepositoryFromDisplayLib,
+    )
+}
 
 val Kosmos.realDisplayRepository by Fixture {
-    DisplayRepositoryImpl(displayRepositoryFromDisplayLib, displayWithDecorationsRepository)
+    DisplayRepositoryImpl(
+        displayRepositoryFromDisplayLib,
+        displayWithDecorationsRepository,
+        displaysWithDecorationsRepositoryFromDisplayLib,
+    )
 }