Merge "[sat] don't show device based icon if any ntn mobile network exists" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index c0a206a..9ad2315 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -49,11 +49,7 @@
     private val dispatcher = StandardTestDispatcher()
     private val testScope = TestScope(dispatcher)
 
-    private val iconsInteractor =
-        FakeMobileIconsInteractor(
-            FakeMobileMappingsProxy(),
-            mock(),
-        )
+    private val iconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
 
     private val repo = FakeDeviceBasedSatelliteRepository()
     private val connectivityRepository = FakeConnectivityRepository()
@@ -515,7 +511,7 @@
 
             // GIVEN, 2 connection
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
-            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
 
             // WHEN all connections are NOT OOS.
             i1.isInService.value = true
@@ -547,7 +543,7 @@
             // GIVEN a condition that should return true (all conections OOS)
 
             val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
-            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
 
             i1.isInService.value = true
             i2.isInService.value = true
@@ -579,4 +575,40 @@
             // THEN the interactor returns true due to the wifi network being active
             assertThat(latest).isTrue()
         }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun isAnyConnectionNtn_trueWhenAnyNtn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+            // GIVEN, 2 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+            // WHEN at least one connection is using ntn
+            i1.isNonTerrestrial.value = true
+            i2.isNonTerrestrial.value = false
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    fun isAnyConnectionNtn_falseWhenNoNtn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isAnyConnectionNtn)
+
+            // GIVEN, 2 connection
+            val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+            val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+
+            // WHEN at no connection is using ntn
+            i1.isNonTerrestrial.value = false
+            i2.isNonTerrestrial.value = false
+
+            // THEN the value is propagated to this interactor
+            assertThat(latest).isFalse()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 509aa7a..fe5b56a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -327,10 +327,11 @@
             // GIVEN satellite is allowed
             repo.isSatelliteAllowedForCurrentLocation.value = true
 
-            // GIVEN all icons are OOS
+            // GIVEN all icons are OOS and not ntn
             val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
             i1.isInService.value = false
             i1.isEmergencyOnly.value = false
+            i1.isNonTerrestrial.value = false
 
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
@@ -344,6 +345,29 @@
         }
 
     @Test
+    fun icon_nullWhenConnected_mobileNtnConnectionExists() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // GIVEN ntn connection exists
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isNonTerrestrial.value = true
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // GIVEN satellite reports that it is Connected
+            repo.connectionState.value = SatelliteConnectionState.On
+
+            // THEN icon is null because despite being connected, the mobile stack is reporting a
+            // nonTerrestrial network, and therefore will have its own icon
+            assertThat(latest).isNull()
+        }
+
+    @Test
     fun icon_satelliteIsProvisioned() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 08a98c3..12f578c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -161,6 +161,13 @@
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
 
+    /** True if any known mobile network is currently using a non terrestrial network */
+    val isAnyConnectionNtn =
+        iconsInteractor.icons.aggregateOver(selector = { it.isNonTerrestrial }, false) {
+            nonTerrestrialNetworks ->
+            nonTerrestrialNetworks.any { it == true }
+        }
+
     companion object {
         const val TAG = "DeviceBasedSatelliteInteractor"
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index f3d5139..ea915ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -114,34 +114,39 @@
 
     private val showIcon =
         if (interactor.isOpportunisticSatelliteIconEnabled) {
-            canShowIcon
-                .flatMapLatest { canShow ->
-                    if (!canShow) {
-                        flowOf(false)
-                    } else {
-                        combine(
-                            shouldShowIconForOosAfterHysteresis,
-                            interactor.connectionState,
-                            interactor.isWifiActive,
-                            airplaneModeRepository.isAirplaneMode,
-                        ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
-                            if (isWifiActive || isAirplaneMode) {
-                                false
-                            } else {
-                                showForOos ||
-                                    connectionState == SatelliteConnectionState.On ||
-                                    connectionState == SatelliteConnectionState.Connected
+                canShowIcon
+                    .flatMapLatest { canShow ->
+                        if (!canShow) {
+                            flowOf(false)
+                        } else {
+                            combine(
+                                shouldShowIconForOosAfterHysteresis,
+                                interactor.isAnyConnectionNtn,
+                                interactor.connectionState,
+                                interactor.isWifiActive,
+                                airplaneModeRepository.isAirplaneMode,
+                            ) { showForOos, anyNtn, connectionState, isWifiActive, isAirplaneMode ->
+                                // anyNtn means that there is some mobile network using ntn, and the
+                                // mobile icon will show its own satellite icon
+                                if (isWifiActive || isAirplaneMode || anyNtn) {
+                                    false
+                                } else {
+                                    // Show for out of service (which has a hysteresis), or ignore
+                                    // the hysteresis if we're already connected
+                                    showForOos ||
+                                        connectionState == SatelliteConnectionState.On ||
+                                        connectionState == SatelliteConnectionState.Connected
+                                }
                             }
                         }
                     }
-                }
-                .distinctUntilChanged()
-                .logDiffsForTable(
-                    tableLog,
-                    columnPrefix = "vm",
-                    columnName = COL_VISIBLE,
-                    initialValue = false,
-                )
+                    .distinctUntilChanged()
+                    .logDiffsForTable(
+                        tableLog,
+                        columnPrefix = "vm",
+                        columnName = COL_VISIBLE,
+                        initialValue = false,
+                    )
             } else {
                 flowOf(false)
             }